Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1077 lines
27 KiB

//-----------------------------------------------------------------------------
// mre.cpp
//
// Copyright (C) 1994, Microsoft Corporation
//
// Purpose:
// implement the MRE* interfaces
//
// Revision History:
//
// [] 02-Nov-1994 Dans Created
//
//-----------------------------------------------------------------------------
#include "pdbimpl.h"
#include "mrimpl.h"
#include <sys/utime.h>
// static data
//
const _TCHAR MRE::c_szMreFileInfo[] = _TEXT("/mr/files/info");
const _TCHAR MRE::c_szMreClassInfo[] = _TEXT("/mr/class/info");
const _TCHAR MRE::c_szMreFileFmt[] = _TEXT("/mr/files/%08lx");
const _TCHAR MRE::c_szMreClassMods[] = _TEXT("/mr/classmods");
const _TCHAR MRE::c_szMreFileMods[] = _TEXT("/mr/unknownfilemods");
const _TCHAR MRE::c_szMreRudeFiles[] = _TEXT("/mr/rudefiles");
const _TCHAR MRE::c_szNoDepHeader[] = _TEXT("//{{NO_DEPENDENCIES}}");
//
// static functions of MREngine
//
MREAPI ( BOOL )
MREngine::FOpen ( OUT PMREngine * ppmre, PPDB ppdb, PNMP pnmp, BOOL fWrite, BOOL fClosePdb ) {
BOOL fRet = fFalse;
precondition ( ppdb );
precondition ( pnmp );
*ppmre = NULL;
MRE * pmre = new MRE;
if ( pmre ) {
MreToPdb mretopdb = { ppdb, pnmp, NULL, NULL, NULL, NULL };
if ( pmre->FInit ( &mretopdb, fWrite, fClosePdb ) ) {
fRet = fTrue;
*ppmre = PMREngine(pmre);
}
else {
delete pmre;
}
}
return fRet;
}
MREAPI ( BOOL )
MREngine::FOpen ( OUT PMREngine * ppmre, PMreToPdb pmretopdb, BOOL fWrite, BOOL fClosePdb ) {
BOOL fRet = fFalse;
precondition ( pmretopdb );
precondition ( pmretopdb->ppdb );
precondition ( pmretopdb->pnamemap );
precondition ( pmretopdb->ptpi );
precondition ( pmretopdb->ppdbPdbFile );
precondition ( pmretopdb->pvReserved1 == NULL );
precondition ( pmretopdb->pvReserved2 == NULL );
*ppmre = NULL;
MRE * pmre = new MRE;
if ( pmre ) {
if ( pmre->FInit ( pmretopdb, fWrite, fClosePdb ) ) {
fRet = fTrue;
*ppmre = PMREngine(pmre);
}
else {
delete pmre;
}
}
return fRet;
}
MREAPI ( BOOL )
MREngine::FOpen (
OUT PMREngine * ppmre,
SZC szPdb,
EC & ec,
_TCHAR szError [ cbErrMax ],
BOOL fReproSig,
BOOL fWrite
) {
BOOL fRet = fFalse;
PPDB ppdb;
SIG sig = fReproSig ? 1 : time(0);
char * szOpenFor = fWrite ? pdbWrite : pdbRead;
*ppmre = NULL;
if ( PDB::OpenEx ( SZ(szPdb), szOpenFor, sig, 1024, &ec, szError, &ppdb ) ) {
PNMP pnmp;
if ( NameMap::open ( ppdb, fWrite, &pnmp ) ) {
if ( !(fRet = MREngine::FOpen ( ppmre, ppdb, pnmp, fWrite, fTrue )) ) {
pnmp->close();
}
}
}
return fRet;
}
//
// MREngine implementation in MRE
//
BOOL
MRE::FInit ( PMreToPdb pmretopdb, BOOL fWrite, BOOL fClosePdb ) {
BOOL fRet = fFalse;
precondition ( pmretopdb );
precondition ( pmretopdb->ppdb );
precondition ( pmretopdb->pnamemap );
// don't assert that we have a ptpi or ppdbPdbFIle, as we have
// cases where we don't need it and it is not supplied.
precondition ( pmretopdb->pvReserved1 == NULL );
precondition ( pmretopdb->pvReserved2 == NULL );
__try {
m_fWrite = fWrite;
m_fClosePdb = fClosePdb;
m_ppdb = pmretopdb->ppdb;
m_pnamemap = pmretopdb->pnamemap;
m_ptpi = pmretopdb->ptpi;
if (!m_ppdb->OpenStream ( SZ(c_szMreFileInfo), &m_pstreamFileInfo ) ||
!m_ppdb->OpenStream ( SZ(c_szMreClassInfo), &m_pstreamClassInfo ) ||
!m_ppdb->OpenStream ( SZ(c_szMreClassMods), &m_pstreamClassMods) ||
!m_ppdb->OpenStream ( SZ(c_szMreFileMods), &m_pstreamFileMods ) ||
!m_ppdb->OpenStream ( SZ(c_szMreRudeFiles), &m_pstreamRudeFiles)
) {
__leave;
}
assert ( m_pstreamFileInfo );
assert ( m_pstreamClassInfo );
assert ( m_pstreamClassMods );
assert ( m_pstreamFileMods );
assert ( m_pstreamRudeFiles );
m_mpnifi.reloadStream ( m_pstreamFileInfo );
m_mpnici.reloadStream ( m_pstreamClassInfo );
m_rgtagniPending.reloadStream ( m_pstreamFileMods );
m_rgtagclsdep.reloadStream ( m_pstreamClassMods );
m_rgtagniRude.reloadStream ( m_pstreamRudeFiles );
// cache away the sizes to see if we have to rewrite the stream
m_itagclsdepSav = m_rgtagclsdep.size();
m_ftagniPendingDirty = fFalse;
m_itagniRudeSav = m_rgtagniRude.size();
// second stage initializations.
m_mrelog.FInit ( this, m_fWrite );
m_lcrechandler.Init ( this );
// now check to see if we have a matching pdb signature or init it if
// we haven't gotten it yet.
if ( pmretopdb->ppdbPdbFile ) {
SIG sigNew = pmretopdb->ppdbPdbFile->QuerySignature();
SIG & sigCur = SigMatchingPdbFile();
if ( sigCur && sigCur != sigNew ) {
FDelete();
}
sigCur = sigNew;
}
fRet = fTrue;
}
__finally {
if ( !fRet ) {
if ( m_pstreamFileInfo ) {
m_pstreamFileInfo->Release();
}
if ( m_pstreamClassInfo ) {
m_pstreamClassInfo->Release();
}
if ( m_pstreamClassMods ) {
m_pstreamClassMods->Release();
}
if ( m_pstreamFileMods ) {
m_pstreamFileMods->Release();
}
if ( m_pstreamRudeFiles ) {
m_pstreamRudeFiles->Release();
}
m_pstreamFileInfo = 0;
m_pstreamClassInfo = 0;
m_pstreamClassMods = 0;
m_pstreamFileMods = 0;
m_pstreamRudeFiles = 0;
m_pnamemap = 0;
m_ppdb = 0;
}
else {
postcondition ( m_pstreamFileInfo );
postcondition ( m_pstreamClassMods );
postcondition ( m_pstreamFileMods );
postcondition ( m_pstreamRudeFiles );
postcondition ( m_pnamemap );
postcondition ( m_ppdb );
}
}
return fRet;
}
BOOL
MRE::FClose ( BOOL fCommit ) {
if ( m_fWrite ) {
Buffer buf;
// save out all of the streams we normally do...
// REVIEW:TODO, only write out streams that have changed.
if ( m_mpnifi.save ( &buf ) ) {
m_pstreamFileInfo->Truncate ( 0 );
m_pstreamFileInfo->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( m_mpnici.save ( &buf ) ) {
m_pstreamClassInfo->Truncate ( 0 );
m_pstreamClassInfo->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( m_itagclsdepSav != m_rgtagclsdep.size() &&
m_rgtagclsdep.save ( &buf )
) {
assert ( m_pstreamClassMods );
m_pstreamClassMods->Truncate ( 0 );
m_pstreamClassMods->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( m_ftagniPendingDirty && m_rgtagniPending.save ( &buf ) ) {
assert ( m_pstreamFileMods );
m_pstreamFileMods->Truncate ( 0 );
m_pstreamFileMods->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( m_itagniRudeSav != m_rgtagniRude.size() &&
m_rgtagniRude.save ( &buf )
) {
assert ( m_pstreamRudeFiles );
m_pstreamRudeFiles->Truncate ( 0 );
m_pstreamRudeFiles->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
}
CloseStream ( m_pstreamFileInfo );
CloseStream ( m_pstreamClassInfo );
CloseStream ( m_pstreamClassMods );
CloseStream ( m_pstreamFileMods );
CloseStream ( m_pstreamRudeFiles );
m_mrelog.Close();
m_lcrechandler.FSerialize();
if ( m_fClosePdb ) {
verify ( m_pnamemap->close() );
if ( fCommit ) {
m_ppdb->Commit();
}
m_ppdb->Close();
m_ppdb = NULL;
m_pnamemap = NULL;
}
delete this;
return fTrue;
}
BOOL
MRE::FCommit() {
if ( m_fWrite ) {
BOOL fRet = fTrue;
Buffer buf;
// save out all of the streams we normally do...
// REVIEW:TODO, only write out streams that have changed.
if ( fRet && (fRet = m_mpnifi.save ( &buf )) ) {
fRet = m_pstreamFileInfo->Truncate ( 0 ) &&
m_pstreamFileInfo->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( fRet && (fRet = m_mpnici.save ( &buf )) ) {
fRet = m_pstreamClassInfo->Truncate ( 0 ) &&
m_pstreamClassInfo->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( m_itagclsdepSav != m_rgtagclsdep.size() &&
fRet && (fRet = m_rgtagclsdep.save ( &buf ))
) {
assert ( m_pstreamClassMods );
fRet = m_pstreamClassMods->Truncate ( 0 ) &&
m_pstreamClassMods->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( m_ftagniPendingDirty &&
fRet && (fRet = m_rgtagniPending.save ( &buf ))
) {
assert ( m_pstreamFileMods );
fRet = m_pstreamFileMods->Truncate ( 0 ) &&
m_pstreamFileMods->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
if ( m_itagniRudeSav != m_rgtagniRude.size() &&
fRet && (fRet = m_rgtagniRude.save ( &buf ))
) {
assert ( m_pstreamRudeFiles );
fRet = m_pstreamRudeFiles->Truncate ( 0 ) &&
m_pstreamRudeFiles->Append ( buf.Start(), buf.Size() );
buf.Reset();
}
return fRet;
}
return fFalse;
}
BOOL
MRE::FDelete() {
precondition ( m_pstreamFileInfo );
precondition ( m_pstreamClassMods );
precondition ( m_pstreamFileMods );
precondition ( m_pstreamRudeFiles );
_TCHAR szStreamName[ ctchMrePathMax ];
Stream * pstream;
EnumMapNiFi e(m_mpnifi);
m_mrelog.LogNote ( "Note: deleting all streams.\n" );
// enumerate all the src file streams
while ( e.next() ) {
PFI pfi;
NI ni;
e.get ( &ni, &pfi );
assert ( ni == pfi->niFile );
if ( pfi->FHasTarget() ) {
pfi->SetFstatus ( fsmOutOfDate );
_sntprintf ( szStreamName, countof(szStreamName), c_szMreFileFmt, ni );
if ( m_ppdb->OpenStream ( szStreamName, &pstream ) ) {
pstream->Truncate ( 0 );
pstream->Release();
}
}
}
// invalidate the current buffer, if any
m_mrfibufRoot.FInitEmpty();
// get rid of the various streams
m_pstreamClassInfo->Truncate ( 0 );
m_pstreamClassMods->Truncate ( 0 );
m_pstreamFileMods->Truncate ( 0 );
m_pstreamRudeFiles->Truncate ( 0 );
// get rid of the line change record data
m_lcrechandler.Delete();
// reset all the in memory copies of the streams
m_mpnici.reset();
m_rgtagniPending.reset();
m_rgtagclsdep.reset();
m_rgtagniRude.reset();
m_itagclsdepSav = m_rgtagclsdep.size();
m_ftagniPendingDirty = fFalse;
m_itagniRudeSav = m_rgtagniRude.size();
return fTrue;
}
BOOL
MRE::FOpenCompiland ( PMREFile * ppmrefile, SZC szFileSrc, SZC szFileTarg ) {
BOOL fRet = fFalse;
_TCHAR szCanonSrc [ ctchMrePathMax ];
_TCHAR szCanonTarg[ ctchMrePathMax ];
*ppmrefile = NULL;
// update the build count in the fileinfo stream
IncBuildId();
if ( m_mrfibufRoot.FInitEmpty() ) {
NI niSrc, niTarg;
niSrc = NiFromName (
SzFullCanonFilename ( szFileSrc, szCanonSrc, countof(szCanonSrc) )
);
niTarg = NiFromName (
SzFullCanonFilename ( szFileTarg, szCanonTarg, countof(szCanonTarg) )
);
m_mrelog.LogCompiland ( niSrc, niTarg );
if ( niSrc != niNil && niTarg != niNil ) {
m_mrfibufRoot.SetNiFile ( niSrc, niTarg );
// insert/update the fileinfo's if necessary.
PFI pfiSrc = NULL;
if ( FFileInfoFromNi ( niSrc, &pfiSrc ) ) {
assert ( pfiSrc->FHasTarget() );
pfiSrc->SetFstatus ( fsmOutOfDate );
m_mrelog.LogCompilandOptions ( pfiSrc->NiOptions() );
}
else {
FI fiSrc(niSrc, fsmHasTarget | fsmOutOfDate, BuildId());
verify ( ::FFileInfo ( szCanonSrc, fiSrc ) );
FInsertFileInfo ( fiSrc );
}
PFI pfiTarg = NULL;
if ( !FFileInfoFromNi ( niTarg, &pfiTarg ) ) {
FI fiTarg(niTarg, fsmIsTarget);
::FFileInfo ( szCanonTarg, fiTarg );
FInsertFileInfo ( fiTarg );
}
// update xref data, must get the pointers now, after all inserts
if ( FFileInfoFromNi ( niSrc, &pfiSrc ) ) {
pfiSrc->SetNiRelated ( niTarg );
}
if ( FFileInfoFromNi ( niTarg, &pfiTarg ) ) {
pfiTarg->SetNiRelated ( niSrc );
}
if ( pfiSrc &&
pfiTarg &&
(m_pmrefRoot = new MREF(this, niSrc))
) {
// ready to go!
*ppmrefile = m_pmrefRoot;
fRet = fTrue;
}
}
}
return fRet;
}
BOOL
MRE::FCloseCompiland ( PMREFile pmrefile, BOOL fCommit ) {
assert ( m_pmrefRoot );
assert ( pmrefile == m_pmrefRoot );
_TCHAR szStreamName[ ctchMrePathMax ];
Stream * pstream;
BOOL fRet = fFalse;
if ( m_mrelog.FLogging() ) {
m_mrelog.LogNote (
"Note: '%s' deps will %sbe committed.\n",
SzFromNi ( m_pmrefRoot->Ni() ),
fCommit ? "" : "not "
);
m_mrelog.LogSep();
}
if ( fCommit ) {
_sntprintf (
szStreamName,
countof(szStreamName),
c_szMreFileFmt,
m_pmrefRoot->Ni()
);
// if we haven't hit all the classes that were noted as mr detectable,
// we need to rude out those classes.
Array<NI> & rgni = m_pmrefRoot->RgNiClassesChanged();
unsigned iniMax = rgni.size();
for ( unsigned ini = 0; ini < iniMax; ini++ ) {
if ( rgni[ ini ] != niNil ) {
// a class we didn't see a different type id for...
PCI pci;
if ( FClassInfoFromNi ( rgni[ ini ], &pci ) ) {
// we have to promote all nested types to rude.
NoteRudeNestedClasses ( pci->Ti() );
}
}
}
if ( m_ppdb->OpenStream ( szStreamName, &pstream ) ) {
// if we need to merge the class dependency data, we need
// to read in the old stream first...
if ( !m_pmrefRoot->FAllCodeCompiled() ) {
MRFIBuf mrfibufT;
mrfibufT.SetPmre ( this );
if ( mrfibufT.FInitFromStream ( pstream ) ) {
assert ( mrfibufT.Pmrfi()->niFile == m_pmrefRoot->Ni() );
m_mrfibufRoot.FmergeClassDeps ( mrfibufT );
}
}
if ( m_mrfibufRoot.FReplaceStream ( pstream ) ) {
fRet = fTrue;
}
else {
pstream->Delete();
}
pstream->Release();
}
if ( fRet ) {
// if everything is ok for the gen'ed dependencies, we
// need to update the build # in the file info stream
PFI pfi = NULL;
verify ( m_mpnifi.map ( m_pmrefRoot->Ni(), &pfi ) );
if ( pfi ) {
pfi->SetBuildId ( BuildId() );
verify ( m_mpnifi.map ( pfi->NiRelated(), &pfi ) );
if ( pfi ) {
pfi->SetBuildId ( BuildId() );
}
}
}
}
else {
fRet = fTrue;
}
delete m_pmrefRoot;
m_pmrefRoot = NULL;
return fRet;
}
BOOL
MRE::FSuccessfulCompile ( BOOL fOk, SZC szFileSrc, SZC szFileTarg ) {
_TCHAR szCanonSrc [ ctchMrePathMax ];
_TCHAR szCanonTarg[ ctchMrePathMax ];
NI niSrc, niTarg;
// get the FILEINFO for the target file and update the time/date/size.
PFI pfiTarg = NULL;
PFI pfiSrc = NULL;
niSrc = NiFromName (
SzFullCanonFilename ( szFileSrc, szCanonSrc, countof(szCanonSrc) )
);
niTarg = NiFromName (
SzFullCanonFilename ( szFileTarg, szCanonTarg, countof(szCanonTarg) )
);
m_mrelog.LogEndCompile ( niSrc, niTarg, fOk );
if ( fOk && FFileInfoFromNi ( niSrc, &pfiSrc ) ) {
assert ( pfiSrc );
assert ( pfiSrc->FHasTarget() );
verify ( FFileInfoFromNi ( niTarg, &pfiTarg ) );
assert ( pfiTarg );
assert ( pfiTarg->FIsTarget() );
if ( pfiTarg && ::FFileInfo ( szCanonTarg, *pfiTarg ) ) {
pfiTarg->ClearFstatus ( fsmOutOfDate );
pfiSrc->ClearFstatus ( fsmOutOfDate );
}
}
// updated fileinfo will get written out during FClose of MREngine
UpdateFileInfoTimeStamp();
return !fOk || (pfiSrc != NULL) && (pfiTarg != NULL);
}
BOOL
MRE::FPushFile ( PMREFile * ppmrefile, SZC szFile, HANDLE hFile ) {
_TCHAR szCanon[ ctchMrePathMax ];
NI ni;
PFI pfi = NULL;
precondition ( szFile );
ni = NiFromName ( SzFullCanonFilename ( szFile, szCanon, countof(szCanon) ) );
m_pmrefRoot->Push ( ni );
m_mrelog.LogPush ( ni );
if ( FFileInfoFromNi ( ni, &pfi ) ) {
assert ( pfi->fiTime != 0 );
assert ( pfi->fiSize != 0 );
pfi->SetBuildId ( BuildId() );
}
else {
// insert a new FILEINFO
FI fi(ni, fsmVisited, BuildId());
// add in the new one after updating the timestamp info
if ( !FInsertFileInfo ( fi ) ) {
// review:error
}
else {
FFileInfoFromNi ( ni, &pfi );
if ( pfi ) {
if ( hFile != INVALID_HANDLE_VALUE ) {
::FFileInfo ( hFile, *pfi );
CheckIgnoreFile ( *pfi, hFile );
}
else {
::FFileInfo ( szCanon, *pfi );
}
}
}
}
// have we visited this file recently?
if ( pfi ) {
if ( FUpdateFileInfo ( pfi, hFile ) ) {
// clear out any status bits that may change how we operate with
// this file in the future
pfi->ClearFstatus ( fsmInclByPch );
}
}
if ( !(pfi && pfi->FIsFsmSet ( fsmIgnoreDep )) ) {
m_mrfibufRoot.FAddFileDep ( ni );
}
*ppmrefile = m_pmrefRoot;
return fTrue;
}
PMREFile
MRE::PmrefilePopFile () {
NI ni = m_pmrefRoot->Pop();
debug ( SZC szFileName = SzFromNi ( ni ) );
m_mrelog.LogPop ( ni );
return m_pmrefRoot;
}
//-----------------------------------------------------------------------------
// MRE::YnmFileOutOfDate
//
// Purpose: tells whether a file is, isn't, or may be out of date
//
// Input: a SRCTARG containing the following fields:
// st.szFileSrc, source file
// st.szFileTarg, target (object) file
// st.szOptions, compiler options it will be built with this time
//
// Output:
// st.dwWeightMaybe, weighting factor for when we return ynmMaybe
//
// Returns:
// ynmNo, ynmYes, or ynmMaybe
//
// Note:
// This method depends on having up-to-date information from
// FRefreshFilesysInfo
//-----------------------------------------------------------------------------
YNM
MRE::YnmFileOutOfDate ( SRCTARG & st ) {
precondition ( st.szSrc );
precondition ( st.szTarg );
YNM ynmRet = ynmYes;
_TCHAR szCanonSrc [ ctchMrePathMax ];
_TCHAR szCanonTarg[ ctchMrePathMax ];
NI niSrc;
NI niTarg;
NI niOptions;
BOOL fUsingPch = fFalse;
st.dwWeightMaybe = 0;
niSrc = NiFromName (
SzFullCanonFilename ( st.szSrc, szCanonSrc, countof(szCanonSrc) )
);
niTarg = NiFromName (
SzFullCanonFilename ( st.szTarg, szCanonTarg, countof(szCanonTarg) )
);
niOptions = st.szOptions ? NiFromName ( st.szOptions ) : niNil;
// find out if it is using a pch...we can safely skip checking files
// that were included in the pch in our dep scan.
if ( st.szOptions ) {
SZC szYu = NULL;
SZC szT = st.szOptions;
// find last " -Yu" in the options. this will skip any in the -D...
// portions and won't match any path name in the includes
// since the driver canonicalizes by lowercasing them.
while ( szT = _tcsstr ( szT, _TEXT(" -Yu") ) ) {
szYu = szT;
szT++;
}
// now, make sure we are not also creating a new pch via a 2-level
// pch create (both -Yc and -Yu on the command line)
szT = st.szOptions;
SZC szYc = NULL;
while ( szT = _tcsstr ( szT, _TEXT(" -Yc") ) ) {
szYc = szT;
szT++;
}
// check to see if are using and not creating a PCH.
fUsingPch = ((szYu != NULL) && (szYc == NULL));
}
PFI pfiSrc = NULL;
if ( FFileInfoFromNi ( niSrc, &pfiSrc ) ) {
if ( !st.fCpp ) {
// not a C++ file anymore, so remove any info we have and bail
m_mpnifi.remove ( niSrc );
m_mpnifi.remove ( niTarg );
return ynmYes;
}
assert ( pfiSrc->niFile == niSrc );
assert ( pfiSrc->FHasTarget() );
// pre-check the source/target pair to see if they are out of date
// in any way. This call will update the outofdate bit if anything
// changed (options, missing src/targ file, src/targ file changes).
CheckSrcTarg ( pfiSrc, niTarg, niOptions );
// if the file is not already marked as out of date, or out of date
// due to rude file changes or class deps, then we have to do some
// more work.
if ( !pfiSrc->FTargOutOfDate() && !FIsFileOutOfDate ( pfiSrc ) ) {
BldId bldidSrc = pfiSrc->BuildId();
unsigned itagni = 0;
// go thru the loop at least once
// 1st iteration checks for any existing pending files that would
// cause us to classify this as a maybe instead of a no.
// 2nd iteration will happen if a new changed file we haven't
// visited yet is included by this source file.
for ( unsigned cIterations = 0; cIterations < 2; cIterations++ ) {
if ( !FLoadMrfi ( pfiSrc->Ni() ) ) {
break;
}
// does the file include any of the "pending
// parse" files?
unsigned itagniMac = m_rgtagniPending.size();
for ( ; itagni < itagniMac; itagni++ ) {
TagNi * ptni = &m_rgtagniPending[ itagni ];
debug ( SZC sz = SzFromNi ( ptni->Ni() ) );
if ( ptni->BuildId() > bldidSrc &&
FNiDependsOnNiFile ( pfiSrc->Ni(), ptni->Ni() )
) {
ynmRet = ynmMaybe;
st.dwWeightMaybe += 1 + m_mrfibufRoot.MapNiClassdep().count();
m_mrelog.LogNote (
"Note: '%s' may be out of date due to changes in '%s'.\n",
szCanonSrc,
SzFromNi ( ptni->Ni() )
);
}
else if ( m_mrelog.FLogging() ) {
m_mrelog.LogNote (
"Note: '%s' not out of date due to '%s' or "
"build id's (%d,%d).\n",
szCanonSrc,
SzFromNi ( ptni->Ni() ),
bldidSrc,
ptni->BuildId()
);
}
}
// if we didn't find any files that need to be compiled,
// we check for any new files that have changed, and check
// again, else we are done.
if ( st.dwWeightMaybe == 0 && !FCheckVisitedDeps ( pfiSrc, fUsingPch ) ) {
ynmRet = ynmNo;
break;
}
}
if ( ynmRet == ynmNo ) {
m_mrelog.LogNote (
"Note: '%s' is not out of date due to current pending changes.\n",
szCanonSrc
);
}
}
}
else {
// file didn't exist in our list. this is our only chance to snarf
// the compile options.
FI fiSrc(niSrc, fsmHasTarget | fsmOutOfDate, BuildId(), niOptions);
FI fiTrg(niTarg, fsmIsTarget | fsmOutOfDate);
// add the file into the list if it exists.
if ( ::FFileInfo ( szCanonSrc, fiSrc ) ) {
::FFileInfo ( szCanonTarg, fiTrg );
FInsertFileInfo ( fiSrc );
FInsertFileInfo ( fiTrg );
FFileInfoFromNi ( niSrc, &pfiSrc );
}
}
if ( pfiSrc && pfiSrc->NiOptions() != niOptions ) {
assert ( ynmRet == ynmYes );
if ( m_mrelog.FLogging() && pfiSrc->NiOptions() != niNil ) {
m_mrelog.LogNote (
"Note: '%s' must be compiled date due to options changing.\n",
SzFromNi ( pfiSrc->Ni() )
);
}
pfiSrc->SetNiOptions ( niOptions );
}
if ( m_mrelog.FLogging() ) {
m_mrelog.LogNote (
"Note: '%s' returning %s\n",
szCanonSrc,
((ynmRet == ynmNo) ? "ynmNo" : ((ynmRet == ynmYes) ? "ynmYes" : "ynmMaybe"))
);
}
return ynmRet;
}
BOOL
MRE::FFilesOutOfDate ( PCAList pcalist ) {
precondition ( pcalist );
m_mrelog.LogSep();
// clear out stale pending files. any pending file's build id
// is compared to the last build id when the file was visited
// (see MRE::FPushFile) to see if it has been compiled yet.
for ( unsigned i = 0; i < m_rgtagniPending.size(); ) {
PFI pfi;
TagNi * ptni = &m_rgtagniPending[ i ];
if ( FFileInfoFromNi ( ptni->Ni(), &pfi ) ) {
if ( ptni->BuildId() <= pfi->BuildId() ) {
if ( m_mrelog.FLogging() ) {
m_mrelog.LogNote (
"Note: changed file '%s' removed from pending list, "
"Build id = %lu, current build id = %lu\n",
SzFromNi ( ptni->Ni() ),
ptni->BuildId(),
pfi->BuildId()
);
}
m_rgtagniPending.deleteAt ( i );
m_ftagniPendingDirty = fTrue;
// don't increment i here since we moved
// everything down with the deleteAt call.
continue;
}
}
i++;
}
// walk the yes list and add all c++ files to end of maybe list
PPSRCTARG ppstMaybeLast = PpstLast ( &pcalist->pstMaybeCompile );
PPSRCTARG ppstNoLast = PpstLast ( &pcalist->pstDontCompile );
PSRCTARG pst = pcalist->pstDoCompile;
PPSRCTARG ppstYesPrev = &pcalist->pstDoCompile;
for ( pst = pcalist->pstDoCompile; pst; pst = PstNext ( pst ) ) {
if ( pst->fCpp ) {
DeleteSrcTargAt ( pst, ppstYesPrev );
ppstMaybeLast = InsertSrcTarg ( ppstMaybeLast, pst );
}
ppstYesPrev = &pst->psrctargNext;
}
// now check all the maybe candidates
unsigned cCppFilesOnYesList = 0;
PPSRCTARG ppstYesLast = PpstLast ( &pcalist->pstDoCompile );
PPSRCTARG ppstMaybePrev = &pcalist->pstMaybeCompile;
PSRCTARG pstNext;
for ( pst = pcalist->pstMaybeCompile; pst; pst = pstNext ) {
pstNext = PstNext ( pst );
switch ( YnmFileOutOfDate ( *pst ) ) {
case ynmNo : {
DeleteSrcTargAt ( pst, ppstMaybePrev );
ppstNoLast = InsertSrcTarg ( ppstNoLast, pst );
break;
}
case ynmYes : {
DeleteSrcTargAt ( pst, ppstMaybePrev );
ppstYesLast = InsertSrcTarg ( ppstYesLast, pst );
cCppFilesOnYesList += pst->fCpp ? 1 : 0;
break;
}
case ynmMaybe : {
ppstMaybePrev = &((*ppstMaybePrev)->psrctargNext);
break;
}
default : {
assert ( fFalse );
break;
}
}
}
// if we put no cpp files onto the yes list, and we have files on the
// maybe list, move the first one to the yes list, otherwise, we are
// done for now.
if ( !cCppFilesOnYesList && (pst = pcalist->pstMaybeCompile) ) {
if ( m_rgtagniPending.size() ) {
DeleteSrcTargAt ( pst, &pcalist->pstMaybeCompile );
InsertSrcTarg ( ppstYesLast, pst );
}
else {
// no pending changes? guess they all can go to the no list
*ppstNoLast = pcalist->pstMaybeCompile;
pcalist->pstMaybeCompile = NULL;
}
}
return fTrue;
}
BOOL
MRE::FUpdateTargetFile ( SZC szTarget, TrgType trgtype ) {
// REVIEW:TODO actually do the patch (novel concept)
_TCHAR szCanon [ ctchMrePathMax ];
NI ni;
PFI pfi;
BOOL fRet = fFalse;
ni = NiFromName (
SzFullCanonFilename ( szTarget, szCanon, countof(szCanon) )
);
if ( trgtype == trgtypeObject && FFileInfoFromNi ( ni, &pfi ) ) {
MREFT mreft;
QWORD cb;
switch ( m_lcrechandler.LcrhrPatchFile ( pfi->BuildId(), szCanon ) ) {
case LCRecHandler::lcrhrFail :
case LCRecHandler::lcrhrNotApplicable : {
_tutime ( szCanon, NULL);
break;
}
case LCRecHandler::lcrhrSuccess : {
break;
}
default : {
notReached();
}
}
IncBuildId();
// update our FILEINFO so that we don't have to deal with those
// changes on our next refresh.
if ( ::FFileInfo ( szCanon, mreft, cb ) ) {
pfi->fiTime = mreft;
pfi->fiSize = cb;
pfi->SetBuildId ( BuildId() );
}
fRet = fTrue;
}
else {
fRet = !_tutime ( szCanon, NULL);
}
m_mrelog.LogNote (
"Note: update of '%s', type = %d, was %ssuccessful.\n",
szCanon,
int(trgtype),
fRet ? "" : "un"
);
return fRet;
}
BOOL
MRE::FRefreshFileSysInfo() {
OneTimeInit();
// do a full file sys refresh always
UpdateFilesAndChangeInfo ( fFalse );
// update our time stamp in the file info stream
UpdateFileInfoTimeStamp();
return fTrue;
}
// Driver calls this once per invocation
void
MRE::OneTimeInit() {
IncBuildId();
m_mrelog.LogSep();
//
// clear out all the visited bits in the FILEINFOs
//
BldId bldidMic = bldidMax;
EnumMapNiFi e(m_mpnifi);
m_mrelog.LogNote ( "Note: clearing visited bits in FILEINFOs.\n" );
while ( e.next() ) {
PFI pfi;
NI niDummy;
e.get ( &niDummy, &pfi );
assert ( niDummy == pfi->niFile );
pfi->ClearFstatus ( fsmVisited );
if ( pfi->FIsTarget() && pfi->BuildId() < bldidMic ) {
bldidMic = pfi->BuildId();
}
}
// get rid of stale line change records
m_lcrechandler.PurgeStaleRecords ( bldidMic );
// skip the file stat'ing but go ahead and apply all known changes
UpdateFilesAndChangeInfo ( fTrue );
}
BOOL
MRE::FStoreDepData ( PDepData pdepdata ) {
m_mrelog.LogNote ( "Note: Storing PCH dependency data.\n" );
if ( m_mrfibufRoot.FStoreIntoDepData ( pdepdata ) ) {
// run through the file deps in the mrfi in order to set the
// "included by PCH" bit
EnumSetNi e(m_mrfibufRoot.SetofNiFile());
NI ni;
PFI pfi;
while ( e.next() ) {
e.get ( &ni );
if ( FFileInfoFromNi ( ni, &pfi ) ) {
pfi->SetFstatus ( fsmInclByPch );
}
}
return fTrue;
}
return fFalse;
}
BOOL
MRE::FRestoreDepData ( PDepData pdepdata ) {
m_mrelog.LogNote ( "Note: Restoring PCH dependency data.\n" );
return m_mrfibufRoot.FInitFromDepData ( pdepdata );
}
void
MRE::ClassIsBoring ( NI niClass ) {
if ( m_mpnici.contains ( niClass ) ) {
m_mpnici.remove ( niClass );
}
m_mrelog.LogBoringClass ( niClass );
}
void
MRE::QueryMreDrv ( PMREDrv & rpmredrv ) {
rpmredrv = PMREDrv(this);
}
void
MRE::QueryMreCmp ( PMRECmp & rpmrecmp, TPI * ptpi ) {
if ( !m_ptpi ) {
m_ptpi = ptpi;
}
rpmrecmp = PMRECmp(this);
}
void
MRE::QueryMreUtil ( PMREUtil & rpmreutil ) {
rpmreutil = PMREUtil(this);
}
BOOL
MRE::FRelease() {
// REVIEW: do we need separate implementations for each of the
// MRE* interfaces?
return fTrue;
}