aboutsummaryrefslogtreecommitdiff
path: root/tools/mhmake/src/mhmakefileparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mhmake/src/mhmakefileparser.cpp')
-rw-r--r--tools/mhmake/src/mhmakefileparser.cpp1082
1 files changed, 1082 insertions, 0 deletions
diff --git a/tools/mhmake/src/mhmakefileparser.cpp b/tools/mhmake/src/mhmakefileparser.cpp
new file mode 100644
index 000000000..cbcb33648
--- /dev/null
+++ b/tools/mhmake/src/mhmakefileparser.cpp
@@ -0,0 +1,1082 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+#include "util.h"
+#include "md5.h"
+
+#include "mhmakefileparser.h"
+#include "rule.h"
+#include "mhmakelexer.h"
+
+///////////////////////////////////////////////////////////////////////////////
+int mhmakefileparser::yylex(void)
+{
+ m_yyloc=m_ptheLexer->m_Line;
+ return m_ptheLexer->yylex(m_theTokenValue);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::yyerror(char *m)
+{
+ cerr << this->m_ptheLexer->m_InputFileName<< " ("<<m_yyloc<<"): "<<m<<endl;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+int mhmakefileparser::ParseFile(const refptr<fileinfo> &FileInfo,bool SetMakeDir)
+{
+ mhmakelexer theLexer;
+ m_ptheLexer=&theLexer;
+ if (SetMakeDir)
+ {
+ m_MakeDir=curdir::GetCurDir();
+ m_Variables[CURDIR]=m_MakeDir->GetFullFileName();
+ }
+ theLexer.m_InputFileName=FileInfo->GetFullFileName();
+ theLexer.m_pParser=(mhmakeparser*)this;
+ theLexer.yyin=::fopen(FileInfo->GetFullFileName().c_str(),"r");
+ if (!theLexer.yyin)
+ {
+ cerr << "Error opening makefile: "<<FileInfo->GetFullFileName()<<endl;
+ return 1;
+ }
+ int Ret=yyparse();
+ ::fclose(theLexer.yyin);
+ return Ret;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::IsDefined(const string &Var) const
+{
+ bool Ret = m_Variables.find(Var)!=m_Variables.end();
+ if (!Ret)
+ {
+ string Env=GetFromEnv(Var);
+ if (!Env.empty())
+ {
+ Ret=true;
+ }
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static inline int SkipUntilQuote(const string &Expr,int i,char Char)
+{
+ while (Expr[i++]!=Char) ;
+ return i;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static inline int SkipMakeExpr(const string &Expr,int i)
+{
+ char Char=Expr[i++];
+ if (Char!='(')
+ return i;
+ Char=Expr[i++];
+ while (Char!=')')
+ {
+ if (Char=='$')
+ {
+ i=SkipMakeExpr(Expr,i);
+ }
+ Char=Expr[i++];
+ }
+ return i;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Splits expression on the Item, but the item may not occur within
+// a macro or quoted string
+static pair<string,string> SplitExpr(const string &Expr,char Item)
+{
+ int i=0;
+ char Char=Expr[i++];
+ while (Char!=Item)
+ {
+ if (Char=='"' || Char=='\'')
+ {
+ i=SkipUntilQuote(Expr,i,Char);
+ }
+ else if (Char=='$')
+ {
+ i=SkipMakeExpr(Expr,i);
+ }
+ Char=Expr[i++];
+ }
+ return pair<string,string>(Expr.substr(0,i-1),Expr.substr(i));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::IsEqual(const string &EqualExpr) const
+{
+ string Expr=ExpandExpression(EqualExpr);
+ const char *pStr=Expr.c_str();
+ const char *pTmp=pStr;
+ while (*pTmp && *pTmp!=',') pTmp++;
+ int Pos=pTmp-pStr;
+ int Size=Expr.size();
+ pTmp=pStr+Size-1;
+ while (pTmp>pStr && strchr(" \t",*pTmp))
+ {
+ pTmp--;
+ }
+ if (2*Pos != pTmp-pStr)
+ {
+ return false;
+ }
+ pTmp=pStr;
+ const char *pTmp2=pTmp+Pos+1;
+ if (*pTmp=='(')
+ {
+ pTmp++;
+ Pos--;
+ }
+ for (int i=0; i<Pos; i++)
+ {
+ if (pTmp[i]!=pTmp2[i])
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandExpression(const string &Expr) const
+{
+ ((mhmakefileparser*)this)->m_InExpandExpression++;
+ int i=0;
+ int Length=Expr.size();
+ string Ret;
+ string ToAdd;
+ while (i<Length)
+ {
+ char Char=Expr[i++];
+ if (Char=='$')
+ {
+ char CharNext=Expr[i];
+ if (CharNext=='$')
+ {
+ ToAdd="$$";
+ i++;
+ }
+ else
+ {
+ int inew=SkipMakeExpr(Expr,i);
+ i++;
+ if (inew>i)
+ {
+ ToAdd=ExpandMacro(Expr.substr(i,inew-i-1));
+ i=inew;
+ }
+ else
+ {
+ // This is a single character expression
+ ToAdd=ExpandMacro(string(1,Expr[i-1]));
+ }
+ }
+ Ret+=ToAdd;
+ }
+ else
+ {
+ Ret+=Char;
+ }
+ }
+ if (m_InExpandExpression==1)
+ {
+ // Here we do a special case in case we still have a $ within a %
+ if (Ret.find('$')!=string::npos)
+ Ret=ExpandExpression(Ret);
+ int Pos;
+ while ((Pos=Ret.find("$$"))!=string::npos)
+ {
+ Ret=Ret.replace(Pos,2,"$");
+ }
+ }
+ ((mhmakefileparser*)this)->m_InExpandExpression--;
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandMacro(const string &Expr) const
+{
+ if (Expr.find('%')!=string::npos && Expr.find('$')==string::npos && Expr.find(':')==string::npos)
+ return string("$(")+Expr+")";
+ string ExpandedExpr=ExpandExpression(Expr);
+
+ const char *pTmp=ExpandedExpr.c_str();
+ /* First remove leading spaces */
+ while (*pTmp==' ' || *pTmp=='\t') pTmp++;
+ const char *pVar=pTmp;
+ while (*pTmp && *pTmp!=' ' && *pTmp!='\t' && *pTmp!=':') pTmp++;
+ const char *pVarEnd=pTmp;
+ char Type=*pTmp++;
+ while (*pTmp && (*pTmp==' ' || *pTmp=='\t')) pTmp++;
+ if (Type&&*pTmp)
+ { // We have a match for the regular expression ^([^ \\t:]+)([: \\t])[ \\t]*(.+)
+ if (Type==':')
+ {
+ string ToSubst=ExpandExpression(ExpandVar(string(pVar,pVarEnd)));
+ const char *pSrc=pTmp;
+ const char *pStop=pSrc;
+ while (*pStop!='=') pStop++;
+ const char *pTo=pStop+1;
+ string SrcStr(pSrc,pStop);
+ string ToStr(pTo);
+ return Substitute(ToSubst,SrcStr,ToStr);
+ }
+ else if (Type==' ' || Type == '\t')
+ {
+ string Func(pVar,pVarEnd);
+ string Arg(pTmp);
+ if (Arg.find('%')!=string::npos && Arg.find('$')!=string::npos)
+ return string("$(")+ExpandedExpr+")";
+ function_f pFunc=m_Functions[Func];
+ #ifdef _DEBUG
+ if (pFunc)
+ {
+ return (this->*pFunc)(Arg);
+ }
+ else
+ {
+ cerr << "Unknown function specified in macro: "<<Func<<endl;
+ throw 1;
+ }
+ #else
+ return (this->*pFunc)(Arg);
+ #endif
+ }
+ else
+ {
+ #ifdef _DEBUG
+ cerr << "Fatal error in ExpandMacro (bug in mhmake ? ? ?)";
+ throw 1;
+ #else
+ return g_EmptyString;
+ #endif
+ }
+ }
+ else
+ {
+ return ExpandExpression(ExpandVar(ExpandedExpr));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandVar(const string &Var) const
+{
+ map<string,string>::const_iterator pIt=m_Variables.find(Var);
+ if (pIt==m_Variables.end())
+ {
+ if (Var.size()==1)
+ {
+ char Char=Var[0];
+ if (m_RuleThatIsBuild)
+ {
+ switch (Char)
+ {
+ case '<': // return first prerequisit
+#ifdef _DEBUG
+ if (!m_RuleThatIsBuild->GetDeps().size())
+ {
+ return "<No Dependencies defined.>";
+ }
+#endif
+ return m_RuleThatIsBuild->GetDeps()[0]->GetFullFileName();
+ case '@': // return full target file name
+ return m_RuleThatIsBuild->GetFullFileName();
+ case '*': // return stem
+ return m_RuleThatIsBuild->GetRule()->GetStem();
+ case '^': // return all prerequisits
+ return m_RuleThatIsBuild->GetPrerequisits();
+ case '/':
+ return OSPATHSEPSTR;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (Char)
+ {
+ case '<': // return first prerequisit
+ case '@': // return full target file name
+ case '*': // return stem
+ case '^': // return all prerequisits
+ return Var; // To make comparing of rules more accurate
+ case '/':
+ return OSPATHSEPSTR;
+ default:
+ break;
+ }
+ }
+ }
+ string Env=GetFromEnv(Var);
+ if (Env.empty())
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ {
+ cout<<"Warning: Variable "<<Var<<" not found\n";
+ }
+ #endif
+ return g_EmptyString;
+ }
+ else
+ return Env;
+ }
+ else
+ return pIt->second;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void SplitToItems(const string &String,vector< refptr<fileinfo> > &Items,refptr<fileinfo> Dir)
+{
+ const char *pTmp=String.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ if (!Item.empty())
+ {
+ if (Item[0]=='"')
+ {
+ Item=Item.substr(1,Item.size()-2);
+ }
+ Items.push_back(GetFileInfo(Item,Dir));
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::ParseBuildedIncludeFiles()
+{
+ vector<string>::iterator It=m_ToBeIncludeAfterBuild.begin();
+ while (It!=m_ToBeIncludeAfterBuild.end())
+ {
+ int result=ParseFile(GetFileInfo(*It));
+ if (result)
+ {
+ fprintf(stderr,"Error parsing %s\n",It->c_str());
+ throw(1);
+ }
+ It++;
+ }
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::PrintVariables(bool Expand) const
+{
+ map<string,string>::const_iterator It=m_Variables.begin();
+ while (It!=m_Variables.end())
+ {
+ if (Expand)
+ {
+ try
+ {
+ cout<<It->first<<" : "<<ExpandExpression(It->second)<<endl;
+ }
+ catch (...)
+ {
+ cout<<endl;
+ }
+ }
+ else
+ {
+ cout<<It->first<<" : "<<It->second<<endl;
+ }
+ It++;
+ }
+ cout << "Exported Variables:\n";
+ vector<string>::const_iterator It2=m_Exports.begin();
+ while (It2!=m_Exports.end())
+ {
+ cout<<*It2<<endl;
+ It2++;
+ }
+}
+#endif
+
+//#define PAGETOSTRING(Nr) #Nr
+//#define PAGETONBR(Nr) PAGETOSTRING(##Nr)
+//#pragma message("ar=" PAGETONBR(NULL) ";")
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::AddRule()
+{
+ vector< refptr<fileinfo> >::iterator pIt=m_pCurrentItems->begin();
+ while (pIt!=m_pCurrentItems->end())
+ {
+ if ((*pIt)->GetFullFileName().find('%')!=string::npos)
+ {
+ IMPLICITRULE::AddImplicitRule(*pIt,*m_pCurrentDeps,m_pCurrentRule);
+ }
+ else
+ {
+ // If we had a double colon we must make sure that the target is always build
+ if (m_DoubleColonRule)
+ {
+ (*pIt)->SetNotExist();
+ }
+ (*pIt)->SetRuleIfNotExist(m_pCurrentRule);
+ if (!m_pCurrentRule)
+ (*pIt)->AddDeps(*m_pCurrentDeps);
+ else
+ (*pIt)->InsertDeps(*m_pCurrentDeps);
+ if (!m_FirstTarget)
+ { // Only check this if the rule is not an implicit rule
+ m_FirstTarget=(*m_pCurrentItems)[0];
+ }
+ }
+ pIt++;
+ }
+ m_pCurrentItems=NULL;
+ m_pCurrentRule=NullRule;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::GetAutoDeps(const refptr<fileinfo> &FirstDep,set< refptr<fileinfo> > &Autodeps)
+{
+ /* Here we have to scan only c/c++ headers so skip certain extensions */
+ const char *pFullName=FirstDep->GetFullFileName().c_str();
+ const char *pExt=strrchr(pFullName,'.');
+ bool bPython=false;
+ if (pExt)
+ {
+ if (!_stricmp(pExt+1,"py"))
+ bPython=true;
+ }
+
+ FILE *pIn=fopen(pFullName,"r");
+ if (!pIn)
+ return;
+
+ refptr<fileinfo> CurDir=curdir::GetCurDir(); /* Since we are calling BuildTarget, the current directory might change, which is not what we should expecting */
+ char IncludeList[255];
+ int PrevRet=0;
+ int Ret=fgetc(pIn);
+ while(Ret!=EOF)
+ {
+ char Type[2];
+ bool bFound=false;
+ if (bPython)
+ {
+ Type[0]='"';
+ if (Ret=='i')
+ {
+ Ret=fscanf(pIn,"mport %254[^\"\n]",IncludeList);
+ if (Ret==1)
+ {
+ if (IncludeList[0]!='*')
+ bFound=true;
+ }
+ }
+ }
+ else
+ {
+ if (PrevRet=='/')
+ {
+ if (Ret=='/')
+ {
+ /* This is a C++ command so read until the next line-feed */
+ do
+ {
+ Ret=fgetc(pIn);
+ } while (Ret!='\n' && Ret!=EOF);
+ }
+ else if (Ret=='*')
+ {
+ /* This is a standard C comment, so read until then end of the command */
+ do
+ {
+ PrevRet=Ret;
+ Ret=fgetc(pIn);
+ } while ((PrevRet!='*' || Ret!='/') && Ret!=EOF);
+ }
+ }
+ else if (Ret=='#' || Ret=='.')
+ {
+ if (Ret=='#')
+ Ret=fscanf(pIn,"include %1[\"<]%254[^>\"]%*[\">]",&Type,IncludeList);
+ else
+ Ret=fscanf(pIn,"import %1[\"<]%254[^>\"]%*[\">]",&Type,IncludeList);
+ if (Ret==2)
+ {
+ bFound=true;
+ }
+ }
+ }
+ if (bFound)
+ {
+ const char *pTmp=IncludeList;
+ while (*pTmp)
+ {
+ string IncludeFile;
+ pTmp=NextItem(pTmp,IncludeFile," \t,");
+ if (bPython)
+ IncludeFile+=".py";
+
+ if (SkipHeaderFile(IncludeFile))
+ continue;
+ #ifdef _DEBUG
+ m_ImplicitSearch++; // This is to avoid warnings of targets that does not exist
+ #endif
+ if (Type[0]=='"')
+ {
+ refptr<fileinfo> pInclude=GetFileInfo(IncludeFile,FirstDep->GetDir());
+ /* Add the dependency when the file alrady exist or there is a rule available to be build */
+ if (BuildTarget(pInclude).DoesExist()) // Try to build the target, and add it if it exists after building
+ {
+ set< refptr<fileinfo> >::iterator pFind=Autodeps.find(pInclude);
+ if (pFind==Autodeps.end())
+ {
+ Autodeps.insert(pInclude);
+ GetAutoDeps(pInclude,Autodeps);
+ }
+ }
+ }
+ const refptr<fileinfoarray> IncludeDirs=GetIncludeDirs();
+ vector< refptr<fileinfo> >::const_iterator It=IncludeDirs->begin();
+ while (It<IncludeDirs->end())
+ {
+ refptr<fileinfo> pInclude=GetFileInfo(IncludeFile,*It);
+ if (BuildTarget(pInclude).DoesExist()) // Try to build the target, and add it if it exists after building
+ {
+ set< refptr<fileinfo> >::iterator pFind=Autodeps.find(pInclude);
+ if (pFind==Autodeps.end())
+ {
+ Autodeps.insert(pInclude);
+ GetAutoDeps(pInclude,Autodeps);
+ }
+ break;
+ }
+ It++;
+ }
+ #ifdef _DEBUG
+ m_ImplicitSearch--;
+ #endif
+ }
+ }
+ PrevRet=Ret;
+ Ret=fgetc(pIn);
+ }
+ fclose(pIn);
+ curdir::ChangeCurDir(CurDir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::UpdateAutomaticDependencies(const refptr<fileinfo> &Target)
+{
+ m_AutoDepsDirty=true; /* Always assume dirty since in the autodeps file, the md5 strings are also saved. */
+ const char *pName=Target->GetFullFileName().c_str();
+ const char *pExt=strrchr(pName,'.');
+ if (!pExt)
+ return;
+ if (Target->IsAutoDepExtention())
+ {
+ // we have to search for the include files in the first dependency of Target
+ vector< refptr<fileinfo> > &Deps=Target->GetDeps();
+ if (!Deps.size())
+ return; // There is no first dep
+ refptr<fileinfo> FirstDep=Deps[0];
+
+ set< refptr<fileinfo> > &Autodeps=m_AutoDeps[Target];
+ Autodeps.clear(); // We are going to scan again, so clear the list
+ GetAutoDeps(FirstDep,Autodeps);
+ // Now add these dependencies also to the rules
+ set< refptr<fileinfo> >::iterator It=Autodeps.begin();
+ while (It!=Autodeps.end())
+ {
+ Target->AddDep(*It);
+ It++;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+const refptr<fileinfoarray> mhmakefileparser::GetIncludeDirs() const
+{
+ string Includes=ExpandExpression("$(INCLUDES)");
+ if (!m_pIncludeDirs || Includes != m_IncludeDirs)
+ {
+ ((mhmakefileparser*)this)->m_IncludeDirs=Includes;
+ ((mhmakefileparser*)this)->m_pIncludeDirs=refptr<fileinfoarray>(new fileinfoarray);
+ if (Includes.empty()) // If not defined try a default path
+ Includes="include inc .."OSPATHSEPSTR"include .."OSPATHSEPSTR"inc";
+ const char *pTmp=Includes.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ refptr<fileinfo> pIncDir=GetFileInfo(Item,m_MakeDir);
+ if (pIncDir->Exists() || pIncDir->GetRule())
+ ((mhmakefileparser*)this)->m_pIncludeDirs->push_back(pIncDir);
+ }
+ }
+ return m_pIncludeDirs;
+}
+
+static void ReadStr(FILE *pFile,char *Str)
+{
+ int i=0;
+ while (1)
+ {
+ Str[i]=fgetc(pFile);
+#ifdef _DEBUG
+ if (Str[i]==EOF)
+ {
+ cout<<"Premature end of depency file.\n";
+ Str[i]=0;
+ return;
+ }
+#endif
+ if (Str[i]=='\n')
+ break;
+ i++;
+ }
+ Str[i]='\0';
+}
+
+void mhmakefileparser::LoadAutoDepsFile(refptr<fileinfo> &DepFile)
+{
+ if (m_AutoDepFileLoaded && m_AutoDepFileLoaded==DepFile)
+ return; /* This autodep file is already loaded. */
+
+ m_AutoDepFileLoaded=DepFile;
+
+ FILE *pIn=fopen(DepFile->GetFullFileName().c_str(),"rb");
+#ifdef _DEBUG
+ if (!pIn)
+ {
+ cerr << "Error opening autodep file "<<DepFile->GetFullFileName()<<endl;
+ return;
+ }
+#endif
+ fread(&m_EnvMd5_32,sizeof(m_EnvMd5_32),1,pIn);
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Reading Env Md5 from "<<DepFile->GetFullFileName()<<": "<<hex<<m_EnvMd5_32<<endl;
+#endif
+ char UsedEnvVars[1024];
+ ReadStr(pIn,UsedEnvVars);
+ SetVariable(USED_ENVVARS,UsedEnvVars);
+
+ char FileName[MAX_PATH];
+ ReadStr(pIn,FileName);
+ while (FileName[0])
+ {
+ refptr<fileinfo> Target=GetFileInfo(FileName);
+ set< refptr<fileinfo> > &Autodeps=m_AutoDeps[Target];
+ ReadStr(pIn,FileName);
+ while (FileName[0])
+ {
+ if (!g_ForceAutoDepRescan) /* If we are forcing the autodepscan we do not have to load the dependencies. */
+ {
+ refptr<fileinfo> Dep=GetFileInfo(FileName);
+ Autodeps.insert(Dep);
+ Target->AddDep(Dep);
+ }
+ ReadStr(pIn,FileName);
+ }
+ ReadStr(pIn,FileName);
+ }
+
+ uint32 Md5_32;
+ while (fread(&Md5_32,sizeof(Md5_32),1,pIn))
+ {
+ ReadStr(pIn,FileName);
+ pair <set<refptr<fileinfo>,less_refptrfileinfo >::iterator, bool> pPair=g_FileInfos.insert(new fileinfo(FileName,Md5_32));
+ if (!pPair.second)
+ {
+ #ifdef _DEBUG
+ if (!(*pPair.first)->CompareMd5_32(Md5_32) && !(*pPair.first)->CompareMd5_32(0))
+ cout << "Warning: trying to set to different md5's for Target "<<(*pPair.first)->GetFullFileName()<<" Old: "<<hex<<(*pPair.first)->GetCommandsMd5_32()<<" New: "<<Md5_32<<endl;
+ if (g_PrintAdditionalInfo)
+ cout << "Setting Md5 for Target "<<(*pPair.first)->GetFullFileName()<<" to "<<hex<<Md5_32<<endl;
+ #endif
+ (*pPair.first)->SetCommandsMd5_32(Md5_32); // If it was already there, just update the md5 value
+ }
+ AddTarget(*pPair.first);
+ }
+
+ fclose(pIn);
+}
+
+static void MakeDirs(const refptr<fileinfo> & Dir)
+{
+ refptr<fileinfo> ParentDir=Dir->GetDir();
+ if (!ParentDir->GetDate().DoesExist())
+ { /* First make parent dirs */
+ MakeDirs(ParentDir);
+ }
+ if (!Dir->GetDate().DoesExist())
+ { /* Create directory */
+ mkdir(Dir->GetFullFileName().c_str(),S_IRWXU);
+ }
+}
+
+void mhmakefileparser::SaveAutoDepsFile()
+{
+ if (!m_AutoDepsDirty)
+ return;
+
+ if (g_Clean
+#ifdef _DEBUG
+ || g_DoNotExecute || g_GenProjectTree
+#endif
+ )
+ return; // do not save on clean or if no commands are executed
+
+ string DepFile=ExpandVar(AUTODEPFILE);
+ if (!DepFile.size())
+ {
+ return;
+ }
+
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout<<"Saving automatic dependency file "<<DepFile<<endl;
+#endif
+
+ FILE *pOut=fopen(DepFile.c_str(),"wb");
+ if (!pOut)
+ {
+ /* Maybe it is because the directory does not exist, so try to create this first */
+ MakeDirs(GetAbsFileInfo(DepFile.substr(0,DepFile.find_last_of(OSPATHSEP))));
+ pOut=fopen(DepFile.c_str(),"wb");
+
+ if (!pOut)
+ {
+ #ifdef _DEBUG
+ if (!g_DoNotExecute)
+ #endif
+ cerr << "Error creating file "<<DepFile<<endl;
+ return;
+ }
+ }
+ // First update the USER_ENVVARS variable and then save it to the dep file together with the md5 string
+ CreateUSED_ENVVARS();
+ uint32 Md5_32=CreateEnvMd5_32();
+ fwrite(&Md5_32,sizeof(Md5_32),1,pOut);
+ fprintf(pOut,"%s\n",m_Variables[USED_ENVVARS].c_str());
+
+ map< refptr<fileinfo>, set< refptr<fileinfo> > >::const_iterator It=m_AutoDeps.begin();
+ while (It!=m_AutoDeps.end())
+ {
+ fprintf(pOut,"%s\n",It->first->GetFullFileName().c_str());
+ set< refptr<fileinfo> >::const_iterator DepIt=It->second.begin();
+ while (DepIt!=It->second.end())
+ {
+ fprintf(pOut,"%s\n",(*DepIt)->GetFullFileName().c_str());
+ DepIt++;
+ }
+ fprintf(pOut,"\n");
+ It++;
+ }
+ /* Now save the Md5 strings */
+ fprintf(pOut,"\n");
+
+ set< const fileinfo * , less_fileinfo>::iterator pIt=m_Targets.begin();
+ while (pIt!=m_Targets.end())
+ {
+ if (!(*pIt)->CompareMd5_32(0))
+ {
+ (*pIt)->WriteMd5_32(pOut);
+ string FileName=(*pIt)->GetFullFileName();
+ fwrite(FileName.c_str(),FileName.size(),1,pOut);
+ fputc('\n',pOut);
+ }
+ pIt++;
+ }
+
+ fclose(pOut);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Uses the SKIPHEADERS variable to check if we have to skip the header in
+// the automatic dependency scanning
+
+bool mhmakefileparser::SkipHeaderFile(const string &FileName)
+{
+ if (!m_SkipHeadersInitialized)
+ {
+ m_SkipHeadersInitialized=true;
+ string HeadersToSkip=ExpandVar(SKIPHEADERS);
+ const char *pTmp=HeadersToSkip.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ if (Item.find('%')==string::npos)
+ {
+ m_SkipHeaders.insert(Item);
+ }
+ else
+ {
+ m_PercentHeaders.push_back(Item);
+ }
+ }
+ }
+ if (m_SkipHeaders.find(FileName)!=m_SkipHeaders.end())
+ return true;
+
+ vector<string>::const_iterator It=m_PercentHeaders.begin();
+ vector<string>::const_iterator ItEnd=m_PercentHeaders.end();
+ while (It!=ItEnd)
+ {
+ if (PercentMatch(FileName,*It++))
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+
+const mhmakefileparser *mhmakefileparser::m_spCurEnv; // Identifies which makefiles exports are currently set
+
+void mhmakefileparser::InitEnv() const
+{
+ if (this!=m_spCurEnv)
+ {
+ /* First restore the previous set environment variables */
+ if (m_spCurEnv)
+ m_spCurEnv->RestoreEnv();
+
+ ((mhmakefileparser*)this)->SaveEnv();
+ vector<string>::const_iterator It=m_Exports.begin();
+ vector<string>::const_iterator ItEnd=m_Exports.end();
+ while (It!=ItEnd)
+ {
+ putenv((char *)It->c_str());
+ It++;
+ }
+ m_spCurEnv=this;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::SaveEnv()
+{
+ vector<string>::const_iterator It=m_Exports.begin();
+ vector<string>::const_iterator ItEnd=m_Exports.end();
+ while (It!=ItEnd)
+ {
+ string Export=*It++;
+ const char *pTmp=Export.c_str();
+ string Var;
+ NextItem(pTmp,Var,"=");
+ const char *pEnv=getenv(Var.c_str());
+ string Env;
+ if (pEnv)
+ Env=pEnv;
+ m_SavedExports[Var]=Env;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::RestoreEnv() const
+{
+ if (m_spCurEnv && this!=m_spCurEnv)
+ {
+ m_spCurEnv->RestoreEnv(); // Make sure that environment variable that were set in the other makefile (m_spCurEnv) are cleared, otherwise the environment is not completely restored.
+ }
+
+ map<string,string>::const_iterator It=m_SavedExports.begin();
+ map<string,string>::const_iterator ItEnd=m_SavedExports.end();
+ while (It!=ItEnd)
+ {
+#ifdef WIN32
+ char Env[1024];
+ sprintf(Env,"%s=%s",It->first.c_str(),It->second.c_str());
+ putenv(Env);
+#else
+ /* on linux, only use putenv if you know the supplied string will stay in memory */
+ setenv(It->first.c_str(),It->second.c_str(),1);
+#endif
+ It++;
+ }
+ ((mhmakefileparser*)this)->m_SavedExports.clear();
+ m_spCurEnv=NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//Create a Md5 string from m_GlobalCommandLineVars and USED_ENVVARS
+//
+
+
+static bool SkipVar(const string &Var)
+{
+ if (Var==WC_REVISION)
+ return true;
+ if (Var==WC_URL)
+ return true;
+ if (Var==MAKE)
+ return true;
+ return false;
+}
+
+#define DBGOUT(stuff)
+
+uint32 mhmakefileparser::CreateEnvMd5_32() const
+{
+ md5_context ctx;
+ string Md5;
+ string EnvVars=ExpandVar(USED_ENVVARS);
+ const char *pTmp=EnvVars.c_str();
+ map<string,string> Variables;
+
+ RestoreEnv(); // Restore the environment before creating the md5, so that we have the original environment when the makefile was parsed.
+
+ /* First create a list of variables to put in the md5 string. This is needed because a variable could be in the
+ * environment and passed on the command-line. This is especially important when switching environments were a certain
+ * variable sometimes is passed with the environment and sometimes on the command line */
+
+ while (*pTmp)
+ {
+ string Var;
+ pTmp=NextItem(pTmp,Var,";");
+ if (!SkipVar(Var))
+ {
+ string Val=GetFromEnv(Var,false);
+ transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
+ transform(Val.begin(),Val.end(),Val.begin(),(int(__CDECL *)(int))toupper);
+ DBGOUT(cout << GetMakeDir()->GetFullFileName() << " -> Setting GetFromEnv var " << Var << " to " << Val << endl);
+ Variables[Var]=Val;
+ }
+ }
+ map<string,string>::const_iterator ItEnd=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.end();
+ map<string,string>::const_iterator It=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.begin();
+ while (It!=ItEnd)
+ {
+ if (!SkipVar(It->first))
+ {
+ string Var=It->first;
+ transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
+ string Val=It->second;
+ transform(Val.begin(),Val.end(),Val.begin(),(int(__CDECL *)(int))toupper);
+ DBGOUT(cout << GetMakeDir()->GetFullFileName() << " -> Setting Commandline var " << Var << " to " << Val << endl);
+ Variables[Var]=Val;
+ }
+ It++;
+ }
+
+ // Now create the md5 string
+ md5_starts( &ctx );
+
+ DBGOUT(cout << "MD5 of " << m_MakeDir->GetFullFileName() << endl);
+
+ map<string,string>::const_iterator VarIt=Variables.begin();
+ map<string,string>::const_iterator VarItEnd=Variables.end();
+ while (VarIt!=VarItEnd)
+ {
+ md5_update( &ctx, (uint8 *) VarIt->first.c_str(), (unsigned long)VarIt->first.size());
+
+ if (!VarIt->second.empty())
+ {
+ md5_update( &ctx, (uint8 *) "=", 1);
+ md5_update( &ctx, (uint8 *) VarIt->second.c_str(), (unsigned long)VarIt->second.size());
+ DBGOUT(cout << VarIt->first << "=" << VarIt->second << endl);
+ }
+ DBGOUT(else cout << VarIt->first << endl;)
+ VarIt++;
+ }
+
+ return md5_finish32( &ctx);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+
+bool mhmakefileparser::CompareEnv() const
+{
+ uint32 Md5_32=CreateEnvMd5_32();
+#ifdef _DEBUG
+ if (!g_GenProjectTree && Md5_32!=m_EnvMd5_32)
+ cout << "Environment has been changed: "<<hex<<m_EnvMd5_32<<" to "<<Md5_32<<endl;
+#endif
+ return Md5_32!=m_EnvMd5_32;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+mh_time_t mhmakefileparser::m_sBuildTime;
+
+void mhmakefileparser::InitBuildTime()
+{
+#ifdef WIN32
+ FILETIME FileTime;
+ GetSystemTimeAsFileTime(&FileTime);
+ m_sBuildTime=g_ZeroTime.ConvertTime(&FileTime);
+#else
+ m_sBuildTime=time(NULL);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Returns a variable from the environment or from the command line and adds it the m_UsedEnvVars
+string mhmakefileparser::GetFromEnv(const string &Var, bool Cache) const
+{
+ /* First we look into the command line variables, before we are looking in the environment */
+ map<string,string>::const_iterator pLineFind=m_CommandLineVars.find(Var);
+ if (pLineFind!=m_CommandLineVars.end())
+ {
+ ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
+ if (Cache)
+ ((mhmakefileparser*)this)->m_Variables[Var]=pLineFind->second;
+ return pLineFind->second;
+ }
+
+ const char *pEnv=getenv(Var.c_str());
+ if (!pEnv)
+ {
+ return g_EmptyString;
+ }
+ ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
+
+ if (Cache)
+ ((mhmakefileparser*)this)->m_Variables[Var]=pEnv;
+ return pEnv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Creates the variable USER_ENVVARS to be saved in the autodeps file
+//
+
+void mhmakefileparser::CreateUSED_ENVVARS()
+{
+ set<string>::const_iterator It=m_UsedEnvVars.begin();
+ set<string>::const_iterator ItEnd=m_UsedEnvVars.end();
+ string Val;
+ while (It!=ItEnd)
+ {
+ Val+=*It;
+ Val+=";";
+ It++;
+ }
+ m_Variables[USED_ENVVARS]=Val;
+}
+