audk/Tools/CCode/Source/MakeDeps/MakeDeps.c

1285 lines
35 KiB
C
Executable File

/*++
Copyright (c) 2004, 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 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
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++;
//
// 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
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 <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
};
for (Index = 0; Str[Index] != NULL; Index++) {
fprintf (stdout, "%s\n", Str[Index]);
}
}