From 24d6ec0750afd0c393bab09aabe174da824a8c56 Mon Sep 17 00:00:00 2001 From: marha Date: Thu, 18 Feb 2010 07:22:26 +0000 Subject: svn merge --reintegrate ^/branches/mhmake.parallel . --- tools/mhmake/src/commandqueue.cpp | 288 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 tools/mhmake/src/commandqueue.cpp (limited to 'tools/mhmake/src/commandqueue.cpp') diff --git a/tools/mhmake/src/commandqueue.cpp b/tools/mhmake/src/commandqueue.cpp new file mode 100644 index 000000000..db14ac38d --- /dev/null +++ b/tools/mhmake/src/commandqueue.cpp @@ -0,0 +1,288 @@ +#include "stdafx.h" + +#include "commandqueue.h" +#include "mhmakeparser.h" + +commandqueue::commandqueue() : + m_NrActiveEntries(0) +{ + SYSTEM_INFO SysInfo; + GetSystemInfo(&SysInfo); + m_MaxNrCommandsInParallel=SysInfo.dwNumberOfProcessors; + + m_pActiveProcesses=new HANDLE[m_MaxNrCommandsInParallel]; + m_pActiveEntries= new activeentry[m_MaxNrCommandsInParallel]; +} + +commandqueue::~commandqueue() +{ + delete [] m_pActiveProcesses; + delete [] m_pActiveEntries; +} + +void commandqueue::SetNrParallelBuilds(unsigned NrParallelBuilds) +{ + if (m_NrActiveEntries) + throw("Changing of number of parallel builds is only allowed when no commands are executing"); + NrParallelBuilds=max(1,NrParallelBuilds); + m_MaxNrCommandsInParallel=NrParallelBuilds; + delete [] m_pActiveProcesses; + delete [] m_pActiveEntries; + m_pActiveProcesses=new HANDLE[NrParallelBuilds]; + m_pActiveEntries= new activeentry[NrParallelBuilds]; +} + +void commandqueue::ThrowCommandExecutionError(activeentry *pActiveEntry) +{ + refptr pTarget=pActiveEntry->pTarget; + const string &Command=pActiveEntry->Command; + mhmakeparser *pMakefile=pTarget->GetRule()->GetMakefile(); + + string ErrorMessage = string("Error running command: ")+ Command +"\n"; + ErrorMessage += "Command defined in makefile: " + pMakefile->GetMakeDir()->GetQuotedFullFileName(); + pTarget->SetCommandsMd5_32(0); /* Clear the md5 to make sure that the target is rebuild the next time mhmake is ran */ + pMakefile->SetAutoDepsDirty(); /* We need to update the autodeps file if the md5 has been changed */ + throw ErrorMessage; +} + +void commandqueue::RemoveActiveEntry(unsigned Entry) +{ +//cout << "Remove entry "<GetQuotedFullFileName()< pTarget=pActiveEntry->pTarget; + mhmakeparser *pMakefile=pTarget->GetRule()->GetMakefile(); + + pMakefile->SetRuleThatIsBuild(pTarget); // Make sure that the command expension is correct + string Command=pMakefile->ExpandExpression(*pActiveEntry->CurrentCommandIt); + pMakefile->ClearRuleThatIsBuild(); /* Make sure that further expansion is not taking this rule into account.*/ + md5_update( &pActiveEntry->md5ctx, (uint8 *)Command.c_str(), (unsigned long)Command.size()); + + pActiveEntry->Command=Command; + + #ifdef _DEBUG + if (g_pPrintDependencyCheck) + { + cout<<"-> "<ExecuteCommand(Command,pActiveEntry->IgnoreError); + if (hProcess==(HANDLE)-1) + { + ThrowCommandExecutionError(pActiveEntry); + } + else if (!hProcess) + { + // Command already finished, so execute next command + return true; + } + else + { + // Command still executing, so add it to the list of handles to wait for + *pActiveProcess=hProcess; + return false; + } + #ifdef _DEBUG + } + #endif + return true; +} + +void commandqueue::TargetBuildFinished(activeentry *pActiveEntry) +{ + refptr pTarget=pActiveEntry->pTarget; + + // Building of this target finished + uint32 Md5_32=md5_finish32( &pActiveEntry->md5ctx); + #ifdef _DEBUG + if (g_DoNotExecute||g_GenProjectTree) + pTarget->SetDateToNow(); + else + #endif + pTarget->InvalidateDate(); + + pTarget->SetCommandsMd5_32(Md5_32); /* If the rule of the target was added with an implicit rule the targets in the rule is empty */ + + refptr pRule=pTarget->GetRule(); + mhmakeparser *pMakefile=pRule->GetMakefile(); + + pMakefile->AddTarget(pTarget); + pMakefile->SetAutoDepsDirty(); + pRule->SetTargetsIsBuild(Md5_32); + + #ifdef _DEBUG + if (g_pPrintDependencyCheck) + { + cout<<"Building "<GetQuotedFullFileName()<<" finished : "<< pTarget->GetDate() << endl; + } + + if (!pMakefile->ImplicitSearch() && !pTarget->Exists() && !pTarget->IsPhony() && !g_DoNotExecute && !g_GenProjectTree) + { + // This is only a warning for phony messages + cout<<"Warning: don't know how to make "<GetQuotedFullFileName()<<"\nMake the rule a phony rule to avoid this warning (but only do it when it is really phony).\n";; + } + #endif + + pTarget->ClearBuilding(); +} + +/* Start executing the commands of a target +*/ +bool commandqueue::StartExecuteCommands(const refptr &pTarget) +{ + cout << "Building " << pTarget->GetQuotedFullFileName()< pRule=pTarget->GetRule(); + mhmakeparser *pMakefile=pRule->GetMakefile(); + vector::iterator CommandIt=pRule->GetCommands().begin(); + activeentry *pActiveEntry=&m_pActiveEntries[m_NrActiveEntries]; + md5_starts( &pActiveEntry->md5ctx ); + + pActiveEntry->pTarget=pTarget; + pActiveEntry->CurrentCommandIt=CommandIt; + pActiveEntry->pTarget=pTarget; + + while (1) + { + if (StartExecuteNextCommand(pActiveEntry, &m_pActiveProcesses[m_NrActiveEntries])) + { + pActiveEntry->CurrentCommandIt++; + if (pActiveEntry->CurrentCommandIt==pRule->GetCommands().end()) + { + // All commands executed + break; + } + } + else + { +//cout << "Adding entry "<pTarget->GetQuotedFullFileName()< &pTarget) +{ + pTarget->SetBuilding(); + // First check if there is place in the active entries + if (m_NrActiveEntries==m_MaxNrCommandsInParallel) + { + // commands cannot be started yet + m_Queue.push(pTarget); + return true; + } + else + { + return !StartExecuteCommands(pTarget); + } +} + +/* Wait for all the commands being executed of a target. In the mean time also continue + executing all other commands in the queue +*/ +mh_time_t commandqueue::WaitForTarget(const refptr &pTarget) +{ + if (!pTarget->IsBuilding()) + return pTarget->GetDate(); + + while (1) + { + // First wait until one of the processes that are running is finished + DWORD Ret=WaitForMultipleObjects(m_NrActiveEntries,m_pActiveProcesses,FALSE,INFINITE); + if (Ret>=m_NrActiveEntries) + throw("fatal error: unexpected return value of WaitForMultipleObjects " + stringify(Ret)); + activeentry *pActiveEntry=&m_pActiveEntries[Ret]; + refptr pCurrentTarget=pActiveEntry->pTarget; + refptr pRule=pCurrentTarget->GetRule(); + + // First check the error code of the command + DWORD ExitCode=0; + HANDLE hProcess=m_pActiveProcesses[Ret]; + if (!GetExitCodeProcess(hProcess,&ExitCode) || ExitCode) + { + if (pActiveEntry->IgnoreError) + { + cerr << "Error running command: "<Command<<", but ignoring error\n"; + } + else + ThrowCommandExecutionError(pActiveEntry); + } + CloseHandle(hProcess); + + // Now check if we have to execute more commands for this target + pActiveEntry->CurrentCommandIt++; + while (pActiveEntry->CurrentCommandIt!=pRule->GetCommands().end()) + { + if (!StartExecuteNextCommand(pActiveEntry, &m_pActiveProcesses[Ret])) + break; // We have to wait for end of command execution + pActiveEntry->CurrentCommandIt++; + } + + if (pActiveEntry->CurrentCommandIt==pRule->GetCommands().end()) + { + TargetBuildFinished(pActiveEntry); + // Target building finished, so remove it from the list of active entries + RemoveActiveEntry(Ret); + + bool Return=false; + if (pTarget==pCurrentTarget) + { + Return = true; + } + // Check if we have other entries in the queue to start + while (1) + { + if (m_Queue.empty()) + { + // There should still be active entries, otherwise this is a serious bug + if (!m_NrActiveEntries && !Return) + throw("Fatal error: WaitForTarget "+pTarget->GetQuotedFullFileName()+": no active targets anymore."); + else + break; + } + else + { + refptr pNewTarget=m_Queue.front(); + m_Queue.pop(); + if (StartExecuteCommands(pNewTarget)) + { + if (pNewTarget==pTarget) + Return=true; + } + else + break; + } + } + if (Return) + return pTarget->GetDate(); + } + } + +} -- cgit v1.2.3