windows-nt/Source/XPSP1/NT/sdktools/build/buildinc.c
2020-09-26 16:20:57 +08:00

541 lines
14 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1989 - 1994.
//
// File: buildinc.c
//
// Contents: This is the checking include module for the NT Build Tool (BUILD.EXE)
//
// Used for detecting the includes that do not satisfy the acceptable
// patterns.
//
// History: see SLM
//----------------------------------------------------------------------------
#include "build.h"
//+---------------------------------------------------------------------------
//
// Function: FoundCountedSequenceInString
//
// Synopsis: Roughly equivalent to "strstr" except that the substring doesn't
// have to be NULL-terminated.
//
// Arguments: [String] -- null-terminated string to search
// [Sequence] -- string to search for
// [Length] -- the length of the sequence
//----------------------------------------------------------------------------
LPCTSTR
FindCountedSequenceInString(
IN LPCTSTR String,
IN LPCTSTR Sequence,
IN DWORD Length
)
{
assert( Sequence );
assert( String );
if ( Length > 0 ) {
while ( *String ) {
while (( *String ) && ( *String != *Sequence )) {
String++;
}
if ( *String ) {
LPCTSTR SubString = String + 1;
LPCTSTR SubSequence = Sequence + 1;
DWORD Remaining = Length - 1;
while (( Remaining ) && ( *SubString++ == *SubSequence++ )) {
Remaining--;
}
if ( Remaining == 0 ) {
return String;
}
String++;
}
}
return NULL;
}
return String;
}
//+---------------------------------------------------------------------------
//
// Function: DoesInstanceMatchPattern
//
// Synopsis: Returns TRUE if pattern matches instance.
// Wildcards:
// * matches any text
// ? matches any and exactly one character
// # matches any text up to backslash character or end of string
//
// Arguments: [Instance] -- the string to be matched
// [Pattern] -- the pattern
//----------------------------------------------------------------------------
BOOL
DoesInstanceMatchPattern(
IN LPCTSTR Instance,
IN LPCTSTR Pattern
)
{
assert( Instance );
assert( Pattern );
while ( *Pattern ) {
if ( *Pattern == TEXT('*')) {
Pattern++;
while ( *Pattern == TEXT('*')) { // skip multiple '*' characters
Pattern++;
}
if ( *Pattern == 0 ) { // '*' at end of pattern matches rest
return TRUE;
}
if ( *Pattern == '?' ) { // '?' following '*'
//
// Expensive because we have to restart match for every
// character position remaining since '?' can match anything.
//
while ( *Instance ) {
if ( DoesInstanceMatchPattern( Instance, Pattern )) {
return TRUE;
}
Instance++;
}
return FALSE;
}
else {
//
// Now we know that next character in pattern is a regular
// character to be matched. Find out the length of that
// string to the next wildcard or end of string.
//
LPCTSTR NextWildCard = Pattern + 1;
DWORD MatchLength;
while (( *NextWildCard ) && ( *NextWildCard != TEXT('*')) && ( *NextWildCard != TEXT('?')) && ( *NextWildCard != TEXT('#'))) {
NextWildCard++;
}
MatchLength = (DWORD)(NextWildCard - Pattern); // always non-zero
//
// Now try to match with any instance of substring in pattern
// found in the instance.
//
Instance = FindCountedSequenceInString( Instance, Pattern, MatchLength );
while ( Instance ) {
if ( DoesInstanceMatchPattern( Instance + MatchLength, NextWildCard )) {
return TRUE;
}
Instance = FindCountedSequenceInString( Instance + 1, Pattern, MatchLength );
}
return FALSE;
}
}
else if ( *Pattern == TEXT('#')) {
//
// Match text up to backslash character or end of string
//
Pattern++;
while (( *Instance != 0 ) && ( *Instance != '\\' )) {
Instance++;
}
continue;
}
else if ( *Pattern == TEXT('?')) {
if ( *Instance == 0 ) {
return FALSE;
}
}
else if ( *Pattern != *Instance ) {
return FALSE;
}
Pattern++;
Instance++;
}
return ( *Instance == 0 );
}
//+---------------------------------------------------------------------------
//
// Function: CombinePaths
//
// Synopsis: Combine two strings to get a full path.
//
// Arguments: [ParentPath] -- head path
// [ChildPath] -- path to be added
// [TargetPath] -- full path
//----------------------------------------------------------------------------
LPSTR
CombinePaths(
IN LPCSTR ParentPath,
IN LPCSTR ChildPath,
OUT LPSTR TargetPath // can be same as ParentPath if want to append
)
{
ULONG ParentLength = strlen( ParentPath );
LPSTR p;
assert( ParentPath );
assert( ChildPath );
if ( ParentPath != TargetPath ) {
memcpy( TargetPath, ParentPath, ParentLength );
}
p = TargetPath + ParentLength;
if (( ParentLength > 0 ) &&
( *( p - 1 ) != '\\' ) &&
( *( p - 1 ) != '/' )) {
*p++ = '\\';
}
strcpy( p, ChildPath );
return TargetPath;
}
//+---------------------------------------------------------------------------
//
// Function: CreateRelativePath
//
// Synopsis: Determine the "canonical" path of one file relative to
// another file
//
// Arguments: [SourceAbsName] -- absolute path of the source file
// [TargetAbsName] -- absolute path of the target file
// [RelativePath] -- resulted relative path
//----------------------------------------------------------------------------
VOID
CreateRelativePath(
IN LPCSTR SourceAbsName, // must be lowercase
IN LPCSTR TargetAbsName, // must be lowercase
OUT LPSTR RelativePath // must be large enough
)
{
//
// First, walk through path components that match in Source and Target.
// For example:
//
// d:\nt\private\ntos\dd\efs.h
// d:\nt\private\windows\base\ntcrypto\des.h
// ^
// This is where the relative path stops going up (..)
// and starts going back down.
//
// So, the "cannonical" relative path generated should look like:
//
// ..\..\..\windows\base\ntcrypto\des.h
//
// For relative includes that are "below" the includer in the path should
// look like this:
//
// .\foo\bar\foobar.h
//
LPCSTR Source = SourceAbsName;
LPCSTR Target = TargetAbsName;
LPSTR Output = RelativePath;
ULONG PathSeparatorIndex;
BOOL AnyParent;
ULONG i;
assert( SourceAbsName );
assert( TargetAbsName );
PathSeparatorIndex = 0;
i = 0;
//
// Scan forward to first non-matching character, and keep track of
// most recent path separator character.
//
while (( Source[ i ] == Target[ i ] ) && ( Source[ i ] != 0 )) {
if ( Source[ i ] == '\\' ) {
PathSeparatorIndex = i;
}
++i;
}
//
// Coming out of this loop, there are 2 possibilities:
//
// 1) Found common ancestor path ( *PathSeparatorIndex == '\\' )
// 2) Don't have common ancestor ( *PathSeparatorIndex != '\\' )
//
if ( Source[ PathSeparatorIndex ] != '\\' ) {
strcpy( RelativePath, TargetAbsName );
return;
}
i = PathSeparatorIndex + 1;
//
// Now continue to walk down source path and insert a "..\" in the result
// for each path separator encountered.
//
AnyParent = FALSE;
while ( Source[ i ] != 0 ) {
if ( Source[ i ] == '\\' ) {
AnyParent = TRUE;
*Output++ = '.';
*Output++ = '.';
*Output++ = '\\';
}
++i;
}
if ( ! AnyParent ) {
//
// Relative path is below current directory.
//
*Output++ = '.';
*Output++ = '\\';
}
//
// Now we simply append what's remaining of the Target path from the
// ancestor match point.
//
strcpy( Output, Target + PathSeparatorIndex + 1 );
}
//+---------------------------------------------------------------------------
//
// Function: ShouldWarnInclude
//
// Synopsis: Returns true if the name of the included file matches a
// BUILD_UNACCEPTABLE_INCLUDES pattern or it does not match
// any of the patterns specified in BUILD_ACCEPTABLE_INCLUDES.
//
// Arguments: [CompilandFullName] -- name of the including file
// [IncludeeFullName] -- name of the included file
//----------------------------------------------------------------------------
BOOL
ShouldWarnInclude(
IN LPCSTR CompilandFullName,
IN LPCSTR IncludeeFullName
)
{
UINT i;
CHAR IncludeeRelativeName[ MAX_PATH ];
assert( CompilandFullName );
assert( IncludeeFullName );
CreateRelativePath( CompilandFullName, IncludeeFullName, IncludeeRelativeName );
//
// First we check for a match against any unacceptable include path
// because we always want to warn about these.
//
for ( i = 0; UnacceptableIncludePatternList[ i ] != NULL; i++ ) {
if ( DoesInstanceMatchPattern( IncludeeFullName, UnacceptableIncludePatternList[ i ] )) {
return TRUE;
}
if ( DoesInstanceMatchPattern( IncludeeRelativeName, UnacceptableIncludePatternList[ i ] )) {
return TRUE;
}
}
//
// If we get to here, the include path was not explicitly unacceptable, so
// we now want to see if it matches any acceptable paths. But, if no
// acceptable paths are specified, we don't want to warn.
//
if ( AcceptableIncludePatternList[ 0 ] == NULL ) {
return FALSE;
}
for ( i = 0; AcceptableIncludePatternList[ i ] != NULL; i++ ) {
if ( DoesInstanceMatchPattern( IncludeeFullName, AcceptableIncludePatternList[ i ] )) {
return FALSE;
}
if ( DoesInstanceMatchPattern( IncludeeRelativeName, AcceptableIncludePatternList[ i ] )) {
return FALSE;
}
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: CheckIncludeForWarning
//
// Synopsis: Warnings if the dependency does not respect the
// BUILD_UNACCEPTABLE_INCLUDES or BUILD_ACCEPTABLE_INCLUDES
// restristions. Works with build -#.
//
// Arguments: [CompilandDir]
// [CompilandName]
// [IncluderDir]
// [IncluderName]
// [IncludeeDir]
// [IncludeeName]
//----------------------------------------------------------------------------
VOID
CheckIncludeForWarning(
IN LPCSTR CompilandDir,
IN LPCSTR CompilandName,
IN LPCSTR IncluderDir,
IN LPCSTR IncluderName,
IN LPCSTR IncludeeDir,
IN LPCSTR IncludeeName
)
{
CHAR CompilandFullName[ MAX_PATH ];
CHAR IncluderFullName[ MAX_PATH ];
CHAR IncludeeFullName[ MAX_PATH ];
assert( CompilandDir );
assert( CompilandName );
assert( IncluderDir );
assert( IncluderName );
assert( IncludeeDir );
assert( IncludeeName );
CombinePaths( CompilandDir, CompilandName, CompilandFullName );
CombinePaths( IncluderDir, IncluderName, IncluderFullName );
CombinePaths( IncludeeDir, IncludeeName, IncludeeFullName );
_strlwr( CompilandFullName );
_strlwr( IncluderFullName );
_strlwr( IncludeeFullName );
if ( IncFile ) {
fprintf(
IncFile,
"%s includes %s\r\n",
IncluderFullName,
IncludeeFullName
);
}
if ( ShouldWarnInclude( CompilandFullName, IncludeeFullName )) {
if ( strcmp( IncluderFullName, CompilandFullName ) == 0 ) {
if ( WrnFile ) {
fprintf(
WrnFile,
"WARNING: %s includes %s\n",
CompilandFullName,
IncludeeFullName
);
}
if ( fShowWarningsOnScreen ) {
BuildMsgRaw(
"WARNING: %s includes %s\n",
CompilandFullName,
IncludeeFullName
);
}
}
else {
if ( WrnFile ) {
fprintf(
WrnFile,
"WARNING: %s includes %s through %s\n",
CompilandFullName,
IncludeeFullName,
IncluderFullName
);
}
if ( fShowWarningsOnScreen ) {
BuildMsgRaw(
"WARNING: %s includes %s through %s\n",
CompilandFullName,
IncludeeFullName,
IncluderFullName
);
}
}
}
}