mirror of https://github.com/acidanthera/audk.git
1285 lines
35 KiB
C
Executable File
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]);
|
|
}
|
|
}
|