| /*++ | |
| Copyright (c) 2004 - 20077, Intel Corporation | |
| All rights reserved. This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| Module Name: | |
| MakeDeps.c | |
| Abstract: | |
| Recursively scan source files to find include files and emit them to | |
| create dependency lists. | |
| --*/ | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdlib.h> | |
| #include <ctype.h> | |
| #include <Common/UefiBaseTypes.h> | |
| #include "EfiUtilityMsgs.h" | |
| #include "CommonLib.h" | |
| // | |
| // Structure to maintain a linked list of strings | |
| // | |
| typedef struct _STRING_LIST { | |
| struct _STRING_LIST *Next; | |
| char *Str; | |
| } STRING_LIST; | |
| #define UTILITY_NAME "MakeDeps" | |
| #define UTILITY_MAJOR_VERSION 1 | |
| #define UTILITY_MINOR_VERSION 0 | |
| #define MAX_LINE_LEN 2048 | |
| #define MAX_PATH 2048 | |
| #define START_NEST_DEPTH 1 | |
| #define MAX_NEST_DEPTH 1000 // just in case we get in an endless loop. | |
| // | |
| // Define the relative paths used by the special #include macros | |
| // | |
| #define PROTOCOL_DIR_PATH "Protocol/" | |
| #define GUID_DIR_PATH "Guid/" | |
| #define ARCH_PROTOCOL_DIR_PATH "ArchProtocol/" | |
| #define PPI_PROTOCOL_DIR_PATH "Ppi/" | |
| // | |
| // Use this structure to keep track of all the special #include forms | |
| // | |
| typedef struct { | |
| INT8 *IncludeMacroName; | |
| INT8 *PathName; | |
| } INCLUDE_MACRO_CONVERSION; | |
| // | |
| // This data is used to convert #include macros like: | |
| // #include EFI_PROTOCOL_DEFINITION(xxx) | |
| // into | |
| // #include Protocol/xxx/xxx.h | |
| // | |
| static const INCLUDE_MACRO_CONVERSION mMacroConversion[] = { | |
| "EFI_PROTOCOL_DEFINITION", | |
| PROTOCOL_DIR_PATH, | |
| "EFI_GUID_DEFINITION", | |
| GUID_DIR_PATH, | |
| "EFI_ARCH_PROTOCOL_DEFINITION", | |
| ARCH_PROTOCOL_DIR_PATH, | |
| "EFI_PROTOCOL_PRODUCER", | |
| PROTOCOL_DIR_PATH, | |
| "EFI_PROTOCOL_CONSUMER", | |
| PROTOCOL_DIR_PATH, | |
| "EFI_PROTOCOL_DEPENDENCY", | |
| PROTOCOL_DIR_PATH, | |
| "EFI_ARCH_PROTOCOL_PRODUCER", | |
| ARCH_PROTOCOL_DIR_PATH, | |
| "EFI_ARCH_PROTOCOL_CONSUMER", | |
| ARCH_PROTOCOL_DIR_PATH, | |
| "EFI_ARCH_PROTOCOL_DEPENDENCY", | |
| ARCH_PROTOCOL_DIR_PATH, | |
| "EFI_PPI_DEFINITION", | |
| PPI_PROTOCOL_DIR_PATH, | |
| "EFI_PPI_PRODUCER", | |
| PPI_PROTOCOL_DIR_PATH, | |
| "EFI_PPI_CONSUMER", | |
| PPI_PROTOCOL_DIR_PATH, | |
| "EFI_PPI_DEPENDENCY", | |
| PPI_PROTOCOL_DIR_PATH, | |
| NULL, | |
| NULL | |
| }; | |
| typedef struct _SYMBOL { | |
| struct _SYMBOL *Next; | |
| INT8 *Name; | |
| INT8 *Value; | |
| } SYMBOL; | |
| // | |
| // Here's all our globals. We need a linked list of include paths, a linked | |
| // list of source files, a linked list of subdirectories (appended to each | |
| // include path when searching), and flags to keep track of command-line options. | |
| // | |
| static struct { | |
| STRING_LIST *IncludePaths; // all include paths to search | |
| STRING_LIST *SourceFiles; // all source files to parse | |
| STRING_LIST *SubDirs; // appended to each include path when searching | |
| SYMBOL *SymbolTable; // for replacement strings | |
| FILE *OutFptr; // output dependencies to this file | |
| BOOLEAN Verbose; // for more detailed output | |
| BOOLEAN IgnoreNotFound; // no warnings if files not found | |
| BOOLEAN QuietMode; // -q - don't print missing file warnings | |
| BOOLEAN NoSystem; // don't process #include <system> files | |
| BOOLEAN NeverFail; // always return success | |
| BOOLEAN NoDupes; // to not list duplicate dependency files (for timing purposes) | |
| BOOLEAN UseSumDeps; // use summary dependency files if found | |
| INT8 TargetFileName[MAX_PATH]; // target object filename | |
| INT8 SumDepsPath[MAX_PATH]; // path to summary files | |
| INT8 *OutFileName; // -o option | |
| } mGlobals; | |
| static | |
| STATUS | |
| ProcessFile ( | |
| INT8 *TargetFileName, | |
| INT8 *FileName, | |
| UINT32 NestDepth, | |
| STRING_LIST *ProcessedFiles | |
| ); | |
| static | |
| FILE * | |
| FindFile ( | |
| INT8 *FileName, | |
| UINT32 FileNameLen | |
| ); | |
| static | |
| void | |
| PrintDependency ( | |
| INT8 *Target, | |
| INT8 *DependentFile | |
| ); | |
| static | |
| void | |
| ReplaceSymbols ( | |
| INT8 *Str, | |
| UINT32 StrSize | |
| ); | |
| static | |
| STATUS | |
| ProcessArgs ( | |
| int Argc, | |
| char *Argv[] | |
| ); | |
| static | |
| void | |
| Version ( | |
| VOID | |
| ); | |
| static | |
| void | |
| Usage ( | |
| VOID | |
| ); | |
| static | |
| void | |
| FreeLists ( | |
| VOID | |
| ); | |
| int | |
| main ( | |
| int Argc, | |
| char *Argv[] | |
| ) | |
| /*++ | |
| Routine Description: | |
| Call the routine to parse the command-line options, then process each file | |
| to build dependencies. | |
| Arguments: | |
| Argc - Standard C main() argc. | |
| Argv - Standard C main() argv. | |
| Returns: | |
| 0 if successful | |
| nonzero otherwise | |
| --*/ | |
| { | |
| STRING_LIST *File; | |
| STRING_LIST ProcessedFiles; | |
| STRING_LIST *TempList; | |
| STATUS Status; | |
| INT8 *Cptr; | |
| INT8 TargetFileName[MAX_PATH]; | |
| SetUtilityName (UTILITY_NAME); | |
| // | |
| // Process the command-line arguments | |
| // | |
| Status = ProcessArgs (Argc, Argv); | |
| if (Status != STATUS_SUCCESS) { | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Go through the list of source files and process each. | |
| // | |
| memset (&ProcessedFiles, 0, sizeof (STRING_LIST)); | |
| File = mGlobals.SourceFiles; | |
| while (File != NULL) { | |
| // | |
| // Clear out our list of processed files | |
| // | |
| TempList = ProcessedFiles.Next; | |
| while (ProcessedFiles.Next != NULL) { | |
| TempList = ProcessedFiles.Next->Next; | |
| free (ProcessedFiles.Next->Str); | |
| free (ProcessedFiles.Next); | |
| ProcessedFiles.Next = TempList; | |
| } | |
| // | |
| // Replace filename extension with ".obj" if they did not | |
| // specifically specify the target file | |
| // | |
| if (mGlobals.TargetFileName[0] == 0) { | |
| strcpy (TargetFileName, File->Str); | |
| // | |
| // Find the .extension | |
| // | |
| for (Cptr = TargetFileName + strlen (TargetFileName) - 1; | |
| (*Cptr != '\\' && *Cptr != '/') && (Cptr > TargetFileName) && (*Cptr != '.'); | |
| Cptr-- | |
| ) | |
| ; | |
| if (Cptr == TargetFileName) { | |
| Error (NULL, 0, 0, File->Str, "could not locate extension in filename"); | |
| goto Finish; | |
| } | |
| // | |
| // Tack on the ".obj" | |
| // | |
| strcpy (Cptr, ".obj"); | |
| } else { | |
| // | |
| // Copy the target filename they specified | |
| // | |
| strcpy (TargetFileName, mGlobals.TargetFileName); | |
| } | |
| Status = ProcessFile (TargetFileName, File->Str, START_NEST_DEPTH, &ProcessedFiles); | |
| if (Status != STATUS_SUCCESS) { | |
| goto Finish; | |
| } | |
| File = File->Next; | |
| } | |
| Finish: | |
| // | |
| // Free up memory | |
| // | |
| FreeLists (); | |
| // | |
| // Free up our processed files list | |
| // | |
| TempList = ProcessedFiles.Next; | |
| while (ProcessedFiles.Next != NULL) { | |
| TempList = ProcessedFiles.Next->Next; | |
| free (ProcessedFiles.Next->Str); | |
| free (ProcessedFiles.Next); | |
| ProcessedFiles.Next = TempList; | |
| } | |
| // | |
| // Close our output file | |
| // | |
| if ((mGlobals.OutFptr != stdout) && (mGlobals.OutFptr != NULL)) { | |
| fprintf(mGlobals.OutFptr, "\t\n"); // file ending flag | |
| fclose (mGlobals.OutFptr); | |
| } | |
| if (mGlobals.NeverFail) { | |
| return STATUS_SUCCESS; | |
| } | |
| // | |
| // If any errors, then delete our output so that it will get created | |
| // again on a rebuild. | |
| // | |
| if ((GetUtilityStatus () == STATUS_ERROR) && (mGlobals.OutFileName != NULL)) { | |
| remove (mGlobals.OutFileName); | |
| } | |
| return GetUtilityStatus (); | |
| } | |
| static | |
| STATUS | |
| ProcessFile ( | |
| INT8 *TargetFileName, | |
| INT8 *FileName, | |
| UINT32 NestDepth, | |
| STRING_LIST *ProcessedFiles | |
| ) | |
| /*++ | |
| Routine Description: | |
| Given a source file name, open the file and parse all #include lines. | |
| Arguments: | |
| TargetFileName - name of the usually .obj target | |
| FileName - name of the file to process | |
| NestDepth - how deep we're nested in includes | |
| ProcessedFiles - list of processed files. | |
| Returns: | |
| standard status. | |
| --*/ | |
| { | |
| FILE *Fptr; | |
| INT8 Line[MAX_LINE_LEN]; | |
| INT8 *Cptr; | |
| INT8 *EndPtr; | |
| INT8 *SaveCptr; | |
| INT8 EndChar; | |
| INT8 FileNameCopy[MAX_PATH]; | |
| INT8 MacroIncludeFileName[MAX_LINE_LEN]; | |
| INT8 SumDepsFile[MAX_PATH]; | |
| STATUS Status; | |
| UINT32 Index; | |
| UINT32 LineNum; | |
| STRING_LIST *ListPtr; | |
| Status = STATUS_SUCCESS; | |
| Fptr = NULL; | |
| // | |
| // Print the file being processed. Indent so you can tell the include nesting | |
| // depth. | |
| // | |
| if (mGlobals.Verbose) { | |
| fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', FileName); | |
| } | |
| // | |
| // If we're using summary dependency files, and a matching .dep file is | |
| // found for this file, then just emit the summary dependency file as | |
| // a dependency and return. | |
| // | |
| if (mGlobals.UseSumDeps) { | |
| strcpy (SumDepsFile, mGlobals.SumDepsPath); | |
| strcat (SumDepsFile, FileName); | |
| for (Cptr = SumDepsFile + strlen (SumDepsFile) - 1; | |
| (*Cptr != '\\' && *Cptr != '/') && (Cptr > SumDepsFile) && (*Cptr != '.'); | |
| Cptr-- | |
| ) | |
| ; | |
| if (*Cptr == '.') { | |
| strcpy (Cptr, ".dep"); | |
| } else { | |
| strcat (SumDepsFile, ".dep"); | |
| } | |
| // | |
| // See if the summary dep file exists. Could use _stat() function, but | |
| // it's less portable. | |
| // | |
| if ((Fptr = fopen (SumDepsFile, "r")) != NULL) { | |
| PrintDependency (TargetFileName, SumDepsFile); | |
| return STATUS_SUCCESS; | |
| } | |
| } | |
| // | |
| // If we're not doing duplicates, and we've already seen this filename, | |
| // then return | |
| // | |
| if (mGlobals.NoDupes) { | |
| for (ListPtr = ProcessedFiles->Next; ListPtr != NULL; ListPtr = ListPtr->Next) { | |
| if (stricmp (FileName, ListPtr->Str) == 0) { | |
| break; | |
| } | |
| } | |
| // | |
| // If we found a match, we're done. If we didn't, create a new element | |
| // and add it to the list. | |
| // | |
| if (ListPtr != NULL) { | |
| // | |
| // Print a message if verbose mode | |
| // | |
| if (mGlobals.Verbose) { | |
| DebugMsg (NULL, 0, 0, FileName, "duplicate include -- not processed again"); | |
| } | |
| return STATUS_SUCCESS; | |
| } | |
| ListPtr = malloc (sizeof (STRING_LIST)); | |
| ListPtr->Str = malloc (strlen (FileName) + 1); | |
| strcpy (ListPtr->Str, FileName); | |
| ListPtr->Next = ProcessedFiles->Next; | |
| ProcessedFiles->Next = ListPtr; | |
| } | |
| // | |
| // Make sure we didn't exceed our maximum nesting depth | |
| // | |
| if (NestDepth > MAX_NEST_DEPTH) { | |
| Error (NULL, 0, 0, FileName, "max nesting depth exceeded on file"); | |
| goto Finish; | |
| } | |
| // | |
| // Make a local copy of the filename. Then we can manipulate it | |
| // if we have to. | |
| // | |
| strcpy (FileNameCopy, FileName); | |
| // | |
| // Try to open the file locally | |
| // | |
| if ((Fptr = fopen (FileNameCopy, "r")) == NULL) { | |
| // | |
| // Try to find it among the paths. | |
| // | |
| Fptr = FindFile (FileNameCopy, sizeof (FileNameCopy)); | |
| if (Fptr == NULL) { | |
| // | |
| // If this is not the top-level file, and the command-line argument | |
| // said to ignore missing files, then return ok | |
| // | |
| if (NestDepth != START_NEST_DEPTH) { | |
| if (mGlobals.IgnoreNotFound) { | |
| if (!mGlobals.QuietMode) { | |
| DebugMsg (NULL, 0, 0, FileNameCopy, "could not find file"); | |
| } | |
| return STATUS_SUCCESS; | |
| } else { | |
| Error (NULL, 0, 0, FileNameCopy, "could not find file"); | |
| return STATUS_ERROR; | |
| } | |
| } else { | |
| // | |
| // Top-level (first) file. Emit an error. | |
| // | |
| Error (NULL, 0, 0, FileNameCopy, "could not find file"); | |
| return STATUS_ERROR; | |
| } | |
| } | |
| } | |
| // | |
| // Print the dependency, with string substitution | |
| // | |
| PrintDependency (TargetFileName, FileNameCopy); | |
| // | |
| // Now read in lines and find all #include lines. Allow them to indent, and | |
| // to put spaces between the # and include. | |
| // | |
| LineNum = 0; | |
| while ((fgets (Line, sizeof (Line), Fptr) != NULL) && (Status == STATUS_SUCCESS)) { | |
| LineNum++; | |
| Cptr = Line; | |
| // | |
| // Skip preceeding spaces on the line | |
| // | |
| while (*Cptr && (isspace (*Cptr))) { | |
| Cptr++; | |
| } | |
| // | |
| // Check for # character | |
| // | |
| if (*Cptr == '#') { | |
| Cptr++; | |
| // | |
| // Check for "include" | |
| // | |
| while (*Cptr && (isspace (*Cptr))) { | |
| Cptr++; | |
| } | |
| if (strncmp (Cptr, "include", 7) == 0) { | |
| // | |
| // Skip over "include" and move on to filename as "file" or <file> | |
| // | |
| Cptr += 7; | |
| while (*Cptr && (isspace (*Cptr))) { | |
| Cptr++; | |
| } | |
| if (*Cptr == '<') { | |
| EndChar = '>'; | |
| } else if (*Cptr == '"') { | |
| EndChar = '"'; | |
| } else { | |
| // | |
| // Handle special #include MACRO_NAME(file) | |
| // Set EndChar to null so we fall through on processing below. | |
| // | |
| EndChar = 0; | |
| // | |
| // Look for all the special include macros and convert accordingly. | |
| // | |
| for (Index = 0; mMacroConversion[Index].IncludeMacroName != NULL; Index++) { | |
| // | |
| // Save the start of the string in case some macros are substrings | |
| // of others. | |
| // | |
| SaveCptr = Cptr; | |
| if (strncmp ( | |
| Cptr, | |
| mMacroConversion[Index].IncludeMacroName, | |
| strlen (mMacroConversion[Index].IncludeMacroName) | |
| ) == 0) { | |
| // | |
| // Skip over the macro name | |
| // | |
| Cptr += strlen (mMacroConversion[Index].IncludeMacroName); | |
| // | |
| // Skip over open parenthesis, blank spaces, then find closing | |
| // parenthesis or blank space | |
| // | |
| while (*Cptr && (isspace (*Cptr))) { | |
| Cptr++; | |
| } | |
| if (*Cptr == '(') { | |
| Cptr++; | |
| while (*Cptr && (isspace (*Cptr))) { | |
| Cptr++; | |
| } | |
| EndPtr = Cptr; | |
| while (*EndPtr && !isspace (*EndPtr) && (*EndPtr != ')')) { | |
| EndPtr++; | |
| } | |
| *EndPtr = 0; | |
| // | |
| // Create the path | |
| // | |
| strcpy (MacroIncludeFileName, mMacroConversion[Index].PathName); | |
| strcat (MacroIncludeFileName, Cptr); | |
| strcat (MacroIncludeFileName, "/"); | |
| strcat (MacroIncludeFileName, Cptr); | |
| strcat (MacroIncludeFileName, ".h"); | |
| // | |
| // Process immediately, then break out of the outside FOR loop. | |
| // | |
| Status = ProcessFile (TargetFileName, MacroIncludeFileName, NestDepth + 1, ProcessedFiles); | |
| break; | |
| } | |
| } | |
| // | |
| // Restore the start | |
| // | |
| Cptr = SaveCptr; | |
| } | |
| // | |
| // Don't recognize the include line? Ignore it. We assume that the | |
| // file compiles anyway. | |
| // | |
| if (mMacroConversion[Index].IncludeMacroName == NULL) { | |
| // | |
| // Warning (FileNameCopy, LineNum, 0, "could not parse line", NULL); | |
| // Status = STATUS_WARNING; | |
| // | |
| } | |
| } | |
| // | |
| // Process "normal" includes. If the endchar is 0, then the | |
| // file has already been processed. Otherwise look for the | |
| // endchar > or ", and process the include file. | |
| // | |
| if (EndChar != 0) { | |
| Cptr++; | |
| EndPtr = Cptr; | |
| while (*EndPtr && (*EndPtr != EndChar)) { | |
| EndPtr++; | |
| } | |
| if (*EndPtr == EndChar) { | |
| // | |
| // If we're processing it, do it | |
| // | |
| if ((EndChar != '>') || (!mGlobals.NoSystem)) { | |
| // | |
| // Null terminate the filename and try to process it. | |
| // | |
| *EndPtr = 0; | |
| Status = ProcessFile (TargetFileName, Cptr, NestDepth + 1, ProcessedFiles); | |
| } | |
| } else { | |
| Warning (FileNameCopy, LineNum, 0, "malformed include", "missing closing %c", EndChar); | |
| Status = STATUS_WARNING; | |
| goto Finish; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| Finish: | |
| // | |
| // Close open files and return status | |
| // | |
| if (Fptr != NULL) { | |
| fclose (Fptr); | |
| } | |
| return Status; | |
| } | |
| static | |
| void | |
| PrintDependency ( | |
| INT8 *TargetFileName, | |
| INT8 *DependentFile | |
| ) | |
| /*++ | |
| Routine Description: | |
| Given a target (.obj) file name, and a dependent file name, do any string | |
| substitutions (per the command line options) on the file names, then | |
| print the dependency line of form: | |
| TargetFileName : DependentFile | |
| Arguments: | |
| TargetFileName - build target file name | |
| DependentFile - file on which TargetFileName depends | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| INT8 Str[MAX_PATH]; | |
| // | |
| // Go through the symbols and do replacements | |
| // | |
| strcpy (Str, DependentFile); | |
| ReplaceSymbols (Str, sizeof (Str)); | |
| fprintf (mGlobals.OutFptr, "%s\n", Str); | |
| } | |
| static | |
| void | |
| ReplaceSymbols ( | |
| INT8 *Str, | |
| UINT32 StrSize | |
| ) | |
| { | |
| SYMBOL *Sym; | |
| INT8 StrCopy[MAX_LINE_LEN]; | |
| INT8 *From; | |
| INT8 *To; | |
| BOOLEAN Replaced; | |
| // | |
| // Go through the entire string to look for replacement strings at | |
| // every position. | |
| // | |
| From = Str; | |
| To = StrCopy; | |
| while (*From) { | |
| // | |
| // Copy the character | |
| // | |
| *To = *From; | |
| Replaced = FALSE; | |
| // | |
| // Go through each symbol and try to find a string substitution | |
| // | |
| Sym = mGlobals.SymbolTable; | |
| while (Sym != NULL) { | |
| if (strnicmp (From, Sym->Value, strlen (Sym->Value)) == 0) { | |
| // | |
| // Replace the string, then advance the pointers past the | |
| // replaced strings | |
| // | |
| strcpy (To, Sym->Name); | |
| To += strlen (Sym->Name); | |
| From += strlen (Sym->Value); | |
| Replaced = TRUE; | |
| // | |
| // Break from the while() | |
| // | |
| break; | |
| } else { | |
| Sym = Sym->Next; | |
| } | |
| } | |
| if (!Replaced) { | |
| From++; | |
| To++; | |
| } | |
| } | |
| // | |
| // Null terminate, and return it | |
| // | |
| *To = 0; | |
| if (strlen (StrCopy) < StrSize) { | |
| strcpy (Str, StrCopy); | |
| } | |
| } | |
| // | |
| // Given a filename, try to find it along the include paths. | |
| // | |
| static | |
| FILE * | |
| FindFile ( | |
| INT8 *FileName, | |
| UINT32 FileNameLen | |
| ) | |
| { | |
| FILE *Fptr; | |
| STRING_LIST *List; | |
| STRING_LIST *SubDir; | |
| INT8 FullFileName[MAX_PATH * 2]; | |
| // | |
| // Traverse the list of paths and try to find the file | |
| // | |
| List = mGlobals.IncludePaths; | |
| while (List != NULL) { | |
| // | |
| // Put the path and filename together | |
| // | |
| if (strlen (List->Str) + strlen (FileName) + 1 > sizeof (FullFileName)) { | |
| Error ( | |
| __FILE__, | |
| __LINE__, | |
| 0, | |
| "application error", | |
| "cannot concatenate '%s' + '%s'", | |
| List->Str, | |
| FileName | |
| ); | |
| return NULL; | |
| } | |
| // | |
| // Append the filename to this include path and try to open the file. | |
| // | |
| strcpy (FullFileName, List->Str); | |
| strcat (FullFileName, FileName); | |
| if ((Fptr = fopen (FullFileName, "r")) != NULL) { | |
| // | |
| // Return the file name | |
| // | |
| if (FileNameLen <= strlen (FullFileName)) { | |
| Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length"); | |
| // | |
| // fprintf (stdout, "File length > %d: %s\n", FileNameLen, FullFileName); | |
| // | |
| return NULL; | |
| } | |
| strcpy (FileName, FullFileName); | |
| return Fptr; | |
| } | |
| // | |
| // Didn't find it there. Now try this directory with every subdirectory | |
| // the user specified on the command line | |
| // | |
| for (SubDir = mGlobals.SubDirs; SubDir != NULL; SubDir = SubDir->Next) { | |
| strcpy (FullFileName, List->Str); | |
| strcat (FullFileName, SubDir->Str); | |
| strcat (FullFileName, FileName); | |
| if ((Fptr = fopen (FullFileName, "r")) != NULL) { | |
| // | |
| // Return the file name | |
| // | |
| if (FileNameLen <= strlen (FullFileName)) { | |
| Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length"); | |
| return NULL; | |
| } | |
| strcpy (FileName, FullFileName); | |
| return Fptr; | |
| } | |
| } | |
| List = List->Next; | |
| } | |
| // | |
| // Not found | |
| // | |
| return NULL; | |
| } | |
| // | |
| // Process the command-line arguments | |
| // | |
| static | |
| STATUS | |
| ProcessArgs ( | |
| int Argc, | |
| char *Argv[] | |
| ) | |
| { | |
| STRING_LIST *NewList; | |
| STRING_LIST *LastIncludePath; | |
| STRING_LIST *LastSourceFile; | |
| SYMBOL *Symbol; | |
| int Index; | |
| // | |
| // Clear our globals | |
| // | |
| memset ((char *) &mGlobals, 0, sizeof (mGlobals)); | |
| mGlobals.NoDupes = TRUE; | |
| // | |
| // Skip program name | |
| // | |
| Argc--; | |
| Argv++; | |
| if (Argc == 0) { | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| if ((strcmp(Argv[0], "-h") == 0) || (strcmp(Argv[0], "--help") == 0) || | |
| (strcmp(Argv[0], "-?") == 0) || (strcmp(Argv[0], "/?") == 0)) { | |
| Usage(); | |
| return STATUS_ERROR; | |
| } | |
| if ((strcmp(Argv[0], "-V") == 0) || (strcmp(Argv[0], "--version") == 0)) { | |
| Version(); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Initialize locals | |
| // | |
| LastIncludePath = NULL; | |
| LastSourceFile = NULL; | |
| // | |
| // Process until no more args | |
| // | |
| while (Argc) { | |
| // | |
| // -i path add include search path | |
| // | |
| if (stricmp (Argv[0], "-i") == 0) { | |
| // | |
| // check for one more arg | |
| // | |
| if (Argc > 1) { | |
| // | |
| // Allocate memory for a new list element, fill it in, and | |
| // add it to our list of include paths. Always make sure it | |
| // has a "\" on the end of it. | |
| // | |
| NewList = malloc (sizeof (STRING_LIST)); | |
| if (NewList == NULL) { | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| NewList->Next = NULL; | |
| NewList->Str = malloc (strlen (Argv[1]) + 2); | |
| if (NewList->Str == NULL) { | |
| free (NewList); | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (NewList->Str, Argv[1]); | |
| if (NewList->Str[strlen (NewList->Str) - 1] != '\\' && NewList->Str[strlen (NewList->Str) - 1] != '/') { | |
| strcat (NewList->Str, "/"); | |
| } | |
| // | |
| // Add it to the end of the our list of include paths | |
| // | |
| if (mGlobals.IncludePaths == NULL) { | |
| mGlobals.IncludePaths = NewList; | |
| } else { | |
| LastIncludePath->Next = NewList; | |
| } | |
| LastIncludePath = NewList; | |
| // | |
| // fprintf (stdout, "Added path: %s\n", NewList->Str); | |
| // | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "option requires an include path"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| Argc--; | |
| Argv++; | |
| } else if (stricmp (Argv[0], "-f") == 0) { | |
| // | |
| // Check for one more arg | |
| // | |
| if (Argc > 1) { | |
| // | |
| // Allocate memory for a new list element, fill it in, and | |
| // add it to our list of source files. | |
| // | |
| NewList = malloc (sizeof (STRING_LIST)); | |
| if (NewList == NULL) { | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| NewList->Next = NULL; | |
| // | |
| // Allocate space to replace ".c" with ".obj", plus null termination | |
| // | |
| NewList->Str = malloc (strlen (Argv[1]) + 5); | |
| if (NewList->Str == NULL) { | |
| free (NewList); | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (NewList->Str, Argv[1]); | |
| if (mGlobals.SourceFiles == NULL) { | |
| mGlobals.SourceFiles = NewList; | |
| } else { | |
| LastSourceFile->Next = NewList; | |
| } | |
| LastSourceFile = NewList; | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "option requires a file name"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // The C compiler first looks for #include files in the directory where | |
| // the source file came from. Add the file's source directory to the | |
| // list of include paths. | |
| // | |
| NewList = malloc (sizeof (STRING_LIST)); | |
| if (NewList == NULL) { | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| NewList->Next = NULL; | |
| NewList->Str = malloc (strlen (Argv[1]) + 3); | |
| if (NewList->Str == NULL) { | |
| free (NewList); | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (NewList->Str, Argv[1]); | |
| // | |
| // Back up in the source file name to the last backslash and terminate after it. | |
| // | |
| for (Index = strlen (NewList->Str) - 1; (Index > 0) && (NewList->Str[Index] != '\\' && NewList->Str[Index] != '/'); Index--) | |
| ; | |
| if (Index < 0) { | |
| strcpy (NewList->Str, "./"); | |
| } else { | |
| NewList->Str[Index + 1] = 0; | |
| } | |
| // | |
| // Add it to the end of the our list of include paths | |
| // | |
| if (mGlobals.IncludePaths == NULL) { | |
| mGlobals.IncludePaths = NewList; | |
| } else { | |
| LastIncludePath->Next = NewList; | |
| } | |
| if (mGlobals.Verbose) { | |
| fprintf (stdout, "Adding include path: %s\n", NewList->Str); | |
| } | |
| LastIncludePath = NewList; | |
| Argc--; | |
| Argv++; | |
| } else if (stricmp (Argv[0], "-s") == 0) { | |
| // | |
| // -s subdir add subdirectory subdir to list of subdirecties to scan. | |
| // Check for one more arg first. | |
| // | |
| if (Argc > 1) { | |
| // | |
| // Allocate memory for a new list element, fill it in, and | |
| // add it to our list of subdirectory include paths. Always | |
| // make sure it has a "\" on the end of it. | |
| // | |
| NewList = malloc (sizeof (STRING_LIST)); | |
| if (NewList == NULL) { | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| NewList->Str = malloc (strlen (Argv[1]) + 2); | |
| if (NewList->Str == NULL) { | |
| free (NewList); | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (NewList->Str, Argv[1]); | |
| if (NewList->Str[strlen (NewList->Str) - 1] != '\\' && NewList->Str[strlen (NewList->Str) - 1] != '/') { | |
| strcat (NewList->Str, "/"); | |
| } | |
| NewList->Next = mGlobals.SubDirs; | |
| mGlobals.SubDirs = NewList; | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "option requires a subdirectory name"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| Argc--; | |
| Argv++; | |
| } else if (stricmp (Argv[0], "-sub") == 0) { | |
| // | |
| // -sub symname symvalue to do string substitution in the output | |
| // | |
| if (Argc > 2) { | |
| // | |
| // Allocate memory for the symbol object | |
| // | |
| Symbol = malloc (sizeof (SYMBOL)); | |
| if (Symbol == NULL) { | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Allocate memory for the symbol name and value, then save copies | |
| // | |
| Symbol->Name = malloc (strlen (Argv[1]) + 1); | |
| if (Symbol->Name == NULL) { | |
| free (Symbol); | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Symbol->Name, Argv[1]); | |
| Symbol->Value = malloc (strlen (Argv[2]) + 1); | |
| if (Symbol->Value == NULL) { | |
| free (Symbol->Name); | |
| free (Symbol); | |
| Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Symbol->Value, Argv[2]); | |
| // | |
| // Add it to the list | |
| // | |
| Symbol->Next = mGlobals.SymbolTable; | |
| mGlobals.SymbolTable = Symbol; | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "option requires a symbol name and value"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Skip over args | |
| // | |
| Argc -= 2; | |
| Argv += 2; | |
| } else if (stricmp (Argv[0], "-nosystem") == 0) { | |
| mGlobals.NoSystem = TRUE; | |
| } else if (stricmp (Argv[0], "-nodupes") == 0) { | |
| mGlobals.NoDupes = TRUE; | |
| } else if (stricmp (Argv[0], "-nodups") == 0) { | |
| mGlobals.NoDupes = TRUE; | |
| } else if (stricmp (Argv[0], "-target") == 0) { | |
| // | |
| // -target TargetFileName - Target object file (only one allowed right | |
| // now) is TargetFileName rather than SourceFile.obj | |
| // | |
| if (Argc > 1) { | |
| strcpy (mGlobals.TargetFileName, Argv[1]); | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "option requires a target file name"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| Argc--; | |
| Argv++; | |
| } else if (stricmp (Argv[0], "-usesumdeps") == 0) { | |
| // | |
| // -usesumdeps Path - if we find an included file xxx.h, and file | |
| // Path/xxx.dep exists, list Path/xxx.dep as a dependency rather than | |
| // xxx.h and don't parse xxx.h. This allows you to create a dependency | |
| // file for a commonly included file, and have its dependency file updated | |
| // only if its included files are updated. Then anyone else including this | |
| // common include file can simply have a dependency on that file's .dep file | |
| // rather than on all the files included by it. Confusing enough? | |
| // | |
| mGlobals.UseSumDeps = 1; | |
| if (Argc > 1) { | |
| strcpy (mGlobals.SumDepsPath, Argv[1]); | |
| // | |
| // Add slash on end if not there | |
| // | |
| if (mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '\\' && mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '/') { | |
| strcat (mGlobals.SumDepsPath, "/"); | |
| } | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "option requires path to summary dependency files"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| Argc--; | |
| Argv++; | |
| } else if (stricmp (Argv[0], "-o") == 0) { | |
| // | |
| // -o OutputFileName - specify an output filename for dependency list | |
| // check for one more arg | |
| // | |
| if (Argc > 1) { | |
| // | |
| // Try to open the file | |
| // | |
| if ((mGlobals.OutFptr = fopen (Argv[1], "w")) == NULL) { | |
| Error (NULL, 0, 0, Argv[1], "could not open file for writing"); | |
| return STATUS_ERROR; | |
| } | |
| mGlobals.OutFileName = Argv[1]; | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "option requires output file name"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| Argc--; | |
| Argv++; | |
| } else if (stricmp (Argv[0], "-v") == 0) { | |
| mGlobals.Verbose = TRUE; | |
| } else if (stricmp (Argv[0], "-neverfail") == 0) { | |
| mGlobals.NeverFail = TRUE; | |
| } else if (stricmp (Argv[0], "-q") == 0) { | |
| mGlobals.QuietMode = TRUE; | |
| } else if (stricmp (Argv[0], "-ignorenotfound") == 0) { | |
| mGlobals.IgnoreNotFound = TRUE; | |
| } else if ((stricmp (Argv[0], "-h") == 0) || (strcmp (Argv[0], "-?") == 0)) { | |
| Usage (); | |
| return STATUS_ERROR; | |
| } else { | |
| Error (NULL, 0, 0, Argv[0], "unrecognized option"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| Argc--; | |
| Argv++; | |
| } | |
| // | |
| // Had to specify at least one source file | |
| // | |
| if (mGlobals.SourceFiles == NULL) { | |
| Error (NULL, 0, 0, "must specify one source file name", NULL); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Assume output to stdout if not specified | |
| // | |
| if (mGlobals.OutFptr == NULL) { | |
| mGlobals.OutFptr = stdout; | |
| } | |
| return STATUS_SUCCESS; | |
| } | |
| // | |
| // Free the global string lists we allocated memory for | |
| // | |
| static | |
| void | |
| FreeLists ( | |
| VOID | |
| ) | |
| { | |
| STRING_LIST *Temp; | |
| SYMBOL *NextSym; | |
| // | |
| // printf ("Free lists....."); | |
| // | |
| // Traverse the include paths, freeing each | |
| // printf ("freeing include paths\n"); | |
| // | |
| while (mGlobals.IncludePaths != NULL) { | |
| Temp = mGlobals.IncludePaths->Next; | |
| // | |
| // printf ("Freeing include path string '%s' at 0x%X\n", | |
| // mGlobals.IncludePaths->Str, (int)(mGlobals.IncludePaths->Str)); | |
| // | |
| free (mGlobals.IncludePaths->Str); | |
| // | |
| // printf ("Freeing include path object at 0x%X\n", (int)(mGlobals.IncludePaths)); | |
| // | |
| free (mGlobals.IncludePaths); | |
| mGlobals.IncludePaths = Temp; | |
| } | |
| // | |
| // Traverse the source files, freeing each | |
| // | |
| while (mGlobals.SourceFiles != NULL) { | |
| Temp = mGlobals.SourceFiles->Next; | |
| free (mGlobals.SourceFiles->Str); | |
| free (mGlobals.SourceFiles); | |
| mGlobals.SourceFiles = Temp; | |
| } | |
| // | |
| // Traverse the subdirectory list, freeing each | |
| // | |
| while (mGlobals.SubDirs != NULL) { | |
| Temp = mGlobals.SubDirs->Next; | |
| free (mGlobals.SubDirs->Str); | |
| free (mGlobals.SubDirs); | |
| mGlobals.SubDirs = Temp; | |
| } | |
| // | |
| // Free the symbol table | |
| // | |
| while (mGlobals.SymbolTable != NULL) { | |
| NextSym = mGlobals.SymbolTable->Next; | |
| free (mGlobals.SymbolTable->Name); | |
| free (mGlobals.SymbolTable->Value); | |
| mGlobals.SymbolTable = NextSym; | |
| } | |
| // | |
| // printf ("done\n"); | |
| // | |
| } | |
| static | |
| void | |
| Version( | |
| void | |
| ) | |
| /*++ | |
| Routine Description: | |
| Displays the standard utility information to SDTOUT | |
| Arguments: | |
| None | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| printf ("%s v%d.%d -Utility for generating file dependency lists for files in a given directory.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION); | |
| printf ("Copyright (c) 1999-2007 Intel Corporation. All rights reserved.\n"); | |
| } | |
| static | |
| void | |
| Usage ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| Print usage information for this utility. | |
| Arguments: | |
| None. | |
| Returns: | |
| Nothing. | |
| --*/ | |
| { | |
| int Index; | |
| static const char *Str[] = { | |
| UTILITY_NAME " -- make dependencies", | |
| " Usage: MakeDeps [options]", | |
| " Options include:", | |
| " -h,--help,-?,/? display help messages", | |
| " -V,--version display version information", | |
| " -f SourceFile add SourceFile to list of files to scan", | |
| " -i IncludePath add IncludePath to list of search paths", | |
| " -o OutputFile write output dependencies to OutputFile", | |
| " -s SubDir for each IncludePath, also search IncludePath\\SubDir", | |
| " -v for verbose output", | |
| " -ignorenotfound don't warn for files not found", | |
| " -target Target for single SourceFile, target is Target, not SourceFile.obj", | |
| " -q quiet mode to not report files not found if ignored", | |
| " -sub sym str replace all occurrances of 'str' with 'sym' in the output", | |
| " -nosystem not process system <include> files", | |
| " -neverfail always return a success return code", | |
| // | |
| // " -nodupes keep track of include files, don't rescan duplicates", | |
| // | |
| " -usesumdeps path use summary dependency files in 'path' directory.", | |
| "", | |
| NULL | |
| }; | |
| Version(); | |
| for (Index = 0; Str[Index] != NULL; Index++) { | |
| fprintf (stdout, "%s\n", Str[Index]); | |
| } | |
| } |