/*++
Copyright (c) 2004 - 2007, 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
#include
#include
#include
#include "Tiano.h"
#include "EfiUtilityMsgs.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 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;
typedef enum {
SearchCurrentDir,
SearchIncludePaths,
SearchAllPaths,
} FILE_SEARCH_TYPE;
//
// 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 *ParentPaths; // all parent 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 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
BOOLEAN IsAsm; // The SourceFiles are assembler files
BOOLEAN IsCl; // The SourceFiles are the output of cl with /showIncludes
INT8 TargetFileName[MAX_PATH]; // target object filename
INT8 SumDepsPath[MAX_PATH]; // path to summary files
INT8 TmpFileName[MAX_PATH]; // temp file name for output file
INT8 *OutFileName; // -o option
} mGlobals;
static
STATUS
ProcessFile (
INT8 *TargetFileName,
INT8 *FileName,
UINT32 NestDepth,
STRING_LIST *ProcessedFiles,
FILE_SEARCH_TYPE FileSearchType
);
static
STATUS
ProcessClOutput (
INT8 *TargetFileName,
INT8 *FileName,
STRING_LIST *ProcessedFiles
);
static
FILE *
FindFile (
INT8 *FileName,
UINT32 FileNameLen,
FILE_SEARCH_TYPE FileSearchType
);
static
void
PrintDependency (
INT8 *Target,
INT8 *DependentFile
);
static
void
ReplaceSymbols (
INT8 *Str,
UINT32 StrSize
);
static
STATUS
ProcessArgs (
int Argc,
char *Argv[]
);
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 > 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);
}
if (mGlobals.IsCl) {
Status = ProcessClOutput (TargetFileName, File->Str, &ProcessedFiles);
} else {
Status = ProcessFile (TargetFileName, File->Str, START_NEST_DEPTH,
&ProcessedFiles, SearchCurrentDir);
}
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 temp output file
//
if ((mGlobals.OutFptr != stdout) && (mGlobals.OutFptr != NULL)) {
fclose (mGlobals.OutFptr);
}
if (mGlobals.NeverFail) {
return STATUS_SUCCESS;
}
if (mGlobals.OutFileName != NULL) {
if (GetUtilityStatus () == STATUS_ERROR) {
//
// If any errors, then delete our temp output
// Also try to delete target file to improve the incremental build
//
remove (mGlobals.TmpFileName);
remove (TargetFileName);
} else {
//
// Otherwise, rename temp file to output file
//
remove (mGlobals.OutFileName);
rename (mGlobals.TmpFileName, mGlobals.OutFileName);
}
}
return GetUtilityStatus ();
}
static
STATUS
ProcessFile (
INT8 *TargetFileName,
INT8 *FileName,
UINT32 NestDepth,
STRING_LIST *ProcessedFiles,
FILE_SEARCH_TYPE FileSearchType
)
/*++
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.
FileSearchType - search type for FileName
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;
STRING_LIST ParentPath;
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 > 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);
fclose (Fptr);
return STATUS_SUCCESS;
}
}
//
// 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);
if (FileSearchType == SearchCurrentDir) {
//
// Try to open the source file locally
//
if ((Fptr = fopen (FileNameCopy, "r")) == NULL) {
Error (NULL, 0, 0, FileNameCopy, "could not open source file");
return STATUS_ERROR;
}
} else {
//
// Try to find it among the paths.
//
Fptr = FindFile (FileNameCopy, sizeof (FileNameCopy), FileSearchType);
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;
}
}
}
//
// 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 (FileNameCopy, 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, FileNameCopy, "duplicate include -- not processed again");
}
fclose (Fptr);
return STATUS_SUCCESS;
}
ListPtr = malloc (sizeof (STRING_LIST));
ListPtr->Str = malloc (strlen (FileNameCopy) + 1);
strcpy (ListPtr->Str, FileNameCopy);
ListPtr->Next = ProcessedFiles->Next;
ProcessedFiles->Next = ListPtr;
}
//
// Print the dependency, with string substitution
//
PrintDependency (TargetFileName, FileNameCopy);
//
// Get the file path and push to ParentPaths
//
Cptr = FileNameCopy + strlen (FileNameCopy) - 1;
for (; (Cptr > FileNameCopy) && (*Cptr != '\\') && (*Cptr != '/'); Cptr--);
if ((*Cptr == '\\') || (*Cptr == '/')) {
*(Cptr + 1) = 0;
} else {
strcpy (FileNameCopy, ".\\");
}
ParentPath.Next = mGlobals.ParentPaths;
ParentPath.Str = FileNameCopy;
mGlobals.ParentPaths = &ParentPath;
//
// 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, there is no # for asm
//
if ((*Cptr == '#') || (mGlobals.IsAsm)) {
if (*Cptr == '#') {
Cptr++;
}
//
// Check for "include", case insensitive for asm
//
while (*Cptr && (isspace (*Cptr))) {
Cptr++;
}
if (((!mGlobals.IsAsm) && (strncmp (Cptr, "include", 7) == 0)) ||
(mGlobals.IsAsm && (_strnicmp (Cptr, "include", 7) == 0))) {
//
// Skip over "include" and move on to filename as "file" or or file for asm
//
Cptr += 7;
while (*Cptr && (isspace (*Cptr))) {
Cptr++;
}
if (*Cptr == '<') {
EndChar = '>';
} else if (*Cptr == '"') {
EndChar = '"';
} else if (mGlobals.IsAsm) {
//
// Handle include file for asm
// Set EndChar to null so we fall through on processing below.
//
EndChar = 0;
//
// Look for the end of include file name
//
EndPtr = Cptr;
while (*EndPtr && (!isspace (*EndPtr))) {
EndPtr++;
}
//
// Null terminate the filename and try to process it.
//
*EndPtr = 0;
Status = ProcessFile (TargetFileName, Cptr, NestDepth + 1,
ProcessedFiles, SearchAllPaths);
} 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, SearchAllPaths);
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 != '>') {
//
// Null terminate the filename and try to process it.
//
*EndPtr = 0;
Status = ProcessFile (TargetFileName, Cptr, NestDepth + 1,
ProcessedFiles, SearchAllPaths);
} else if (!mGlobals.NoSystem) {
//
// Null terminate the filename and try to process it.
//
*EndPtr = 0;
Status = ProcessFile (TargetFileName, Cptr, NestDepth + 1,
ProcessedFiles, SearchIncludePaths);
}
} else {
Warning (FileNameCopy, LineNum, 0, "malformed include", "missing closing %c", EndChar);
Status = STATUS_WARNING;
goto Finish;
}
}
}
}
}
//
// Pop the file path from ParentPaths
//
mGlobals.ParentPaths = ParentPath.Next;
Finish:
//
// Close open files and return status
//
if (Fptr != NULL) {
fclose (Fptr);
}
return Status;
}
static
STATUS
ProcessClOutput (
INT8 *TargetFileName,
INT8 *FileName,
STRING_LIST *ProcessedFiles
)
/*++
Routine Description:
Given a source file name, open the file and parse all "Note: including file: xxx.h" lines.
Arguments:
TargetFileName - name of the usually .obj target
FileName - name of the file to process
ProcessedFiles - list of processed files.
Returns:
standard status.
--*/
{
FILE *Fptr;
INT8 Line[MAX_LINE_LEN];
INT8 IncludeFileName[MAX_LINE_LEN];
STRING_LIST *ListPtr;
BOOLEAN ClError;
INT32 Ret;
INT8 Char;
if ((Fptr = fopen (FileName, "r")) == NULL) {
Error (NULL, 0, 0, FileName, "could not open file for reading");
return STATUS_ERROR;
}
if (fgets (Line, sizeof (Line), Fptr) != NULL) {
//
// First line is the source file name, print it
//
printf ("%s", Line);
} else {
//
// No output from cl
//
fclose (Fptr);
Error (NULL, 0, 0, NULL, "incorrect cl tool path may be used ");
return STATUS_ERROR;
}
ClError = FALSE;
while (fgets (Line, sizeof (Line), Fptr) != NULL) {
Ret = sscanf (Line, "Note: including file: %s %c", IncludeFileName, &Char);
if (Ret == 2) {
//
// There is space in include file name. It's VS header file. Ignore it.
//
continue;
} else if ( Ret != 1) {
//
// Cl error info, print it
// the tool will return error code to stop the nmake
//
ClError = TRUE;
printf ("%s", Line);
continue;
}
//
// If we're not doing duplicates, and we've already seen this filename,
// then continue
//
if (mGlobals.NoDupes) {
for (ListPtr = ProcessedFiles->Next; ListPtr != NULL; ListPtr = ListPtr->Next) {
if (_stricmp (IncludeFileName, 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, IncludeFileName, "duplicate include -- not processed again");
}
continue;
}
ListPtr = malloc (sizeof (STRING_LIST));
ListPtr->Str = malloc (strlen (IncludeFileName) + 1);
strcpy (ListPtr->Str, IncludeFileName);
ListPtr->Next = ProcessedFiles->Next;
ProcessedFiles->Next = ListPtr;
}
PrintDependency (TargetFileName, IncludeFileName);
}
fclose (Fptr);
if (ClError) {
Error (NULL, 0, 0, NULL, "cl error");
return STATUS_ERROR;
} else {
return STATUS_SUCCESS;
}
}
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, TargetFileName);
ReplaceSymbols (Str, sizeof (Str));
fprintf (mGlobals.OutFptr, "%s : ", Str);
strcpy (Str, DependentFile);
ReplaceSymbols (Str, sizeof (Str));
fprintf (mGlobals.OutFptr, "%s\n", Str);
//
// Add pseudo target to avoid incremental build failure when the file is deleted
//
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_SEARCH_TYPE FileSearchType
)
{
FILE *Fptr;
STRING_LIST *List;
STRING_LIST *SubDir;
INT8 FullFileName[MAX_PATH * 2];
//
// Traverse the list of paths and try to find the file
//
if (FileSearchType == SearchAllPaths) {
List = mGlobals.ParentPaths;
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;
}
List = List->Next;
}
}
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;
//
// Clear our globals
//
memset ((char *) &mGlobals, 0, sizeof (mGlobals));
mGlobals.NoDupes = TRUE;
//
// Skip program name
//
Argc--;
Argv++;
//
// 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] != '\\') {
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;
}
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] != '\\') {
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] != '\\') {
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) {
mGlobals.OutFileName = Argv[1];
//
// Use temp file for output
// This can avoid overwriting previous existed dep file when error
// ocurred in this tool
//
sprintf (mGlobals.TmpFileName, "%s2", mGlobals.OutFileName);
//
// Try to open the temp file
//
if ((mGlobals.OutFptr = fopen (mGlobals.TmpFileName, "w")) == NULL) {
Error (NULL, 0, 0, mGlobals.TmpFileName, "could not open file for writing");
return STATUS_ERROR;
}
} 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], "-asm") == 0) {
if (mGlobals.IsCl) {
Error (NULL, 0, 0, Argv[0], "option conflict with -cl");
return STATUS_ERROR;
}
mGlobals.IsAsm = TRUE;
} else if (_stricmp (Argv[0], "-cl") == 0) {
if (mGlobals.IsAsm) {
Error (NULL, 0, 0, Argv[0], "option conflict with -asm");
return STATUS_ERROR;
}
mGlobals.IsCl = 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
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 or -? for this help 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 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.",
" -asm The SourceFiles are assembler files",
" -cl The SourceFiles are the output of cl with /showIncludes",
"",
NULL
};
for (Index = 0; Str[Index] != NULL; Index++) {
fprintf (stdout, "%s\n", Str[Index]);
}
}