/* 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 .
*/
/* $Rev$ */
#ifndef __MHMAKEFILE_PARSER__
#define __MHMAKEFILE_PARSER__
#include "fileinfo.h"
#include "util.h"
#include "commandqueue.h"
class rule;
class mhmakelexer;
struct TOKENVALUE
{
string theString;
int ival;
};
class mhmakefileparser;
typedef string (mhmakefileparser::*function_f)(const string &) const;
struct funcdef
{
const char *szFuncName;
function_f pFunc;
};
class fileinfoarray : public refbase,public vector >
{
};
typedef set< refptr > deps_t;
typedef pair< bool, deps_t > autodeps_entry_t;
typedef map< refptr, autodeps_entry_t > autodeps_t;
class mhmakefileparser : public refbase
{
private:
static commandqueue sm_CommandQueue;
mhmakelexer *m_ptheLexer;
int m_yyloc;
refptr m_RuleThatIsBuild;
vector m_ToBeIncludeAfterBuild;
vector m_MakefilesToLoad;
refptr m_AutoDepFileLoaded;
int m_InExpandExpression;
mh_time_t m_Date;
uint32 m_EnvMd5_32; /* Cached Md5_32 value of the userd environment variables */
#ifdef _DEBUG
int m_ImplicitSearch;
#endif
map m_CommandCache;
protected:
map m_Variables;
map m_CommandLineVars;
TOKENVALUE m_theTokenValue;
refptr m_MakeDir;
refptr m_pCurrentRule;
refptr m_pCurrentItems;
refptr m_pCurrentDeps;
refptr m_FirstTarget;
fileinfoarray m_IncludedMakefiles;
refptr m_pIncludeDirs;
string m_IncludeDirs;
bool m_DoubleColonRule;
bool m_AutoDepsDirty;
bool m_ForceAutoDepRescan;
set m_SkipHeaders; // Headers to skip
vector m_PercentHeaders; // Percent specification of headers to skip
bool m_SkipHeadersInitialized; // true when previous 2 variables are initialized
bool m_RebuildAll; /* true when to rebuild all targets of this makefile */
set m_UsedEnvVars; // Array containing a list of variables that are taken from the environment (This is used for rebuild checking)
set m_Exports; // Array containing a list of exported variables in the makefile (This is used for rebuild checking)
#ifdef WIN32
char *m_pEnv; // New environment in case the makefile exports variables
#else
char **m_pEnv; // New environment in case the makefile exports variables
#endif
size_t m_EnvLen; // Current length of m_pEnv
autodeps_t m_AutoDeps;
set< const fileinfo* , less_fileinfo > m_Targets; // List of targets that are build by this makefile
static mh_time_t m_sBuildTime;
private:
string ResolveExpression(const string &InExpr,string &Rest) const;
public:
#ifdef _DEBUG
deque< refptr > m_TargetStack; /* Used to detect circular dependencies */
#endif
mhmakefileparser(const map &CommandLineVars)
: m_CommandLineVars(CommandLineVars)
,m_InExpandExpression(0)
,m_AutoDepsDirty(false)
,m_ForceAutoDepRescan(false)
,m_SkipHeadersInitialized(false)
,m_RebuildAll(false)
,m_EnvMd5_32(0)
,m_pEnv(NULL)
#ifdef _DEBUG
,m_ImplicitSearch(0)
#endif
{
if (!m_FunctionsInitialised) InitFuncs();
SetVariable("MAKE_VERSION",MHMAKEVER);
SetVariable(OBJEXTVAR,OBJEXT);
SetVariable(EXEEXTVAR,EXEEXT);
}
/* Needed if you only want to use the searchcommand and execommand functions */
mhmakefileparser(const refptr &pMakeDir) :
m_MakeDir(pMakeDir)
, m_AutoDepsDirty(false)
, m_pEnv(NULL)
{}
static void SetNrParallelBuilds(int NrParallelBuilds)
{
sm_CommandQueue.SetNrParallelBuilds(NrParallelBuilds);
}
bool CompareEnv() const;
uint32 CreateEnvMd5_32() const;
string GetFromEnv(const string &Var,bool Cache=true) const;
void CreateUSED_ENVVARS();
void SetExport(const string & Var, const string & Val);
void CheckEnv(void);
#ifdef _DEBUG
int ImplicitSearch() const
{
return m_ImplicitSearch;
}
#endif
void SetAutoDepsDirty()
{
m_AutoDepsDirty=true;
}
void ClearAutoDepsDirty()
{
m_AutoDepsDirty=false;
}
bool IsAutoDepsDirty() const
{
return m_AutoDepsDirty;
}
void SetRebuildAll()
{
m_RebuildAll=true;
SetAutoDepsDirty(); /* This is to be sure that all new calculated md5 strings are saved. */
}
void SetVariable(string Var,string Val)
{
m_Variables[Var]=Val;
}
void EnableAutoDepRescan(void)
{
m_ForceAutoDepRescan=true;
SetAutoDepsDirty();
}
bool ForceAutoDepRescan(void)
{
return m_ForceAutoDepRescan;
}
void SetRuleThatIsBuild(const refptr &Target)
{
m_RuleThatIsBuild=Target;
}
void ClearRuleThatIsBuild()
{
m_RuleThatIsBuild=NULL;
}
void GetAutoDepsIfNeeded(const refptr &Target, const refptr&FirstDep);
void UpdateAutomaticDependencies(const refptr &Target);
void UpdateNoRuleAutomaticDependencies(const refptr &Target);
const refptr GetIncludeDirs() const;
void GetAutoDeps(const refptr &FirstDep, deps_t &Autodeps);
void SaveAutoDepsFile();
void LoadAutoDepsFile(refptr &DepFile);
bool SkipHeaderFile(const string &FileName);
void InitEnv() const;
virtual ~mhmakefileparser()
{
SaveAutoDepsFile();
#ifndef WIN32
char **pEnv=m_pEnv;
if (pEnv)
while (*pEnv) free(*pEnv++);
#endif
free(m_pEnv);
}
virtual int yylex(void);
virtual void yyerror(const char *m);
virtual int yyparse()
{
throw("Please derive if you want to execute yyparse.");
}
int ParseFile(const refptr &FileInfo,refptr &pMakeDir=NullFileInfo);
/* Functions to handle variables */
bool IsDefined(const string &Var) const;
bool IsEqual(const string &EqualExpr) const;
bool IsExprTrue(const string &EqualExpr) const;
string ExpandExpression(const string &Expr) const;
string ExpandMacro(const string &Expr) const;
string ExpandVar(const string &Var) const;
void PrintVariables(bool Expand=false) const;
/* Functions for macro functions */
static funcdef m_FunctionsDef[];
static map m_Functions;
static bool m_FunctionsInitialised;
static void InitFuncs(void);
string f_filter(const string &) const;
string f_call(const string &) const;
string f_if(const string &) const;
string f_findstring(const string &) const;
string f_firstword(const string &) const;
string f_wildcard(const string &) const;
string f_subst(const string &) const;
string f_patsubst(const string &) const;
string f_concat(const string &) const;
string f_basename(const string & Arg) const;
string f_notdir(const string & Arg) const;
string f_dir(const string & Arg) const;
string f_shell(const string & Arg) const;
string f_relpath(const string & Arg) const;
string f_toupper(const string & Arg) const;
string f_tolower(const string & Arg) const;
string f_exist(const string & Arg) const;
string f_filesindirs(const string & Arg) const;
string f_fullname(const string & Arg) const;
string f_addprefix(const string & Arg) const;
string f_addsuffix(const string & Arg) const;
string f_filterout(const string & Arg) const;
string f_word(const string & Arg) const;
string f_words(const string & Arg) const;
string f_strip(const string & Arg) const;
string f_which(const string & Arg) const;
const refptr GetFirstTarget() const
{
return m_FirstTarget;
}
const refptr GetMakeDir() const
{
return m_MakeDir;
}
mh_time_t GetDate(void) const
{
return m_Date;
}
void UpdateDate(mh_time_t Date)
{
if (Date.IsNewer(m_Date))
m_Date=Date;
}
void AddTarget(const fileinfo* pTarget)
{
m_Targets.insert(pTarget);
}
/* Starts building the target,
returns 0 when target build is not finished,
returns the date of the target when target is build, especially important for phony rules, since this will be the youngest date of all dependencies */
mh_time_t StartBuildTarget(const refptr &Target, bool bCheckTargetDir=true);
/* Waits for the target being build, returns the date of the target. Not needed to be cald when StartBuildTarget returned a value different from zero */
mh_time_t WaitBuildTarget(const refptr &Target);
/* Use the following command when you want to wait for the target is built */
mh_time_t BuildTarget(const refptr &Target, bool bCheckTargetDir=true)
{
mh_time_t TargetDate=StartBuildTarget(Target,bCheckTargetDir);
if (!TargetDate.IsDateValid())
return WaitBuildTarget(Target);
else
return TargetDate;
}
void BuildDependencies(const refptr &pRule, const refptr &Target, mh_time_t TargetDate, mh_time_t &YoungestDate, bool &MakeTarget);
void BuildIncludedMakefiles();
void AddIncludedMakefile(refptr &MakeInfo)
{
UpdateDate(MakeInfo->GetDate());
m_IncludedMakefiles.push_back(MakeInfo);
}
fileinfoarray &GetIncludedMakefiles()
{
return m_IncludedMakefiles;
}
void IncludeAfterBuild(const string &IncludeFile)
{
m_ToBeIncludeAfterBuild.push_back(IncludeFile);
}
void ParseBuildedIncludeFiles();
void AddMakefileToMakefilesToLoad(const string &Makefile)
{
m_MakefilesToLoad.push_back(Makefile);
}
vector& GetMakefilesToLoad()
{
return m_MakefilesToLoad;
}
void AddRule();
mh_pid_t ExecuteCommand(string Command, bool &IgnoreError, string *pOutput=NULL);
mh_pid_t ExecuteCommand(string Command, string *pOutput=NULL)
{
bool IgnoreError;
return ExecuteCommand(Command, IgnoreError, pOutput);
}
string GetFullCommand(string Command);
void CreatePythonExe(const string &FullCommand);
static void InitBuildTime();
void SplitToItems(const string &String, vector< refptr > &Items) const;
mh_pid_t DeleteFiles(const string &Params) const;
mh_pid_t CopyFiles(const string &Params) const;
mh_pid_t TouchFiles(const string &Params) const;
mh_pid_t EchoCommand(const string &Params) const;
string SearchCommand(const string &Command, const string &Extension="") const;
const string &GetPythonExe() const;
int SearchPath(const char *szCommand, const char *pExt, size_t Len, char *szFullCommand,char **pFilePart) const;
mh_pid_t OsExeCommand(const string &Command, const string &Params, bool IgnoreError, string *pOutput) const;
};
#endif