#include <sdk.h>
#include <wx/log.h>
#include <wx/intl.h>
#include <wx/filename.h>
#include <wx/msgdlg.h>
#include <wx/stream.h>
#include <wx/wfstream.h>
#include <wx/txtstrm.h>
#include <wx/regex.h> // used in QUICK hack at line 574
#include <compiler.h>
#include <cbproject.h>
#include <projectbuildtarget.h>
#include <globals.h>
#include <manager.h>
#include <messagemanager.h>
#include <macrosmanager.h>
#include "directcommands.h"
#include "compilergcc.h"
#include "makefilegenerator.h"
#include "customvars.h"
#include <depslib.h>
pfDetails::pfDetails(DirectCommands* cmds, ProjectBuildTarget* target, ProjectFile* pf)
{
wxString sep = wxFileName::GetPathSeparator();
wxFileName tmp;
wxFileName prjbase(cmds->m_pProject->GetBasePath());
source_file_native = pf->relativeFilename;
source_file_absolute_native = pf->file.GetFullPath();
tmp = pf->GetObjName();
if (FileTypeOf(pf->relativeFilename) == ftHeader)
object_file_native = pf->GetObjName(); // support for precompiled headers
else
object_file_native = (target ? target->GetObjectOutput() : _T(".")) +
sep +
tmp.GetFullPath();
wxFileName o_file(object_file_native);
o_file.MakeAbsolute(prjbase.GetFullPath());
object_dir_native = o_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
object_file_absolute_native = o_file.GetFullPath();
tmp.SetExt(_T("depend"));
dep_file_native = (target ? target->GetDepsOutput() : _T(".")) +
sep +
tmp.GetFullPath();
wxFileName d_file(dep_file_native);
d_file.MakeAbsolute(prjbase.GetFullPath());
dep_dir_native = d_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
dep_file_absolute_native = o_file.GetFullPath();
source_file = UnixFilename(source_file_native);
cmds->QuoteStringIfNeeded(source_file);
object_file = UnixFilename(object_file_native);
cmds->QuoteStringIfNeeded(object_file);
dep_file = UnixFilename(dep_file_native);
cmds->QuoteStringIfNeeded(dep_file);
object_dir = UnixFilename(object_dir_native);
cmds->QuoteStringIfNeeded(object_dir);
dep_dir = UnixFilename(dep_dir_native);
cmds->QuoteStringIfNeeded(dep_dir);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(object_file_native);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(object_dir_native);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(object_file_absolute_native);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(dep_file_native);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(dep_dir_native);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(dep_file_absolute_native);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(dep_dir);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(object_dir);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(dep_file);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(object_file);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(source_file);
}
DirectCommands::DirectCommands(CompilerGCC* compilerPlugin, Compiler* compiler, cbProject* project, int logPageIndex)
: m_PageIndex(logPageIndex),
m_pCompilerPlugin(compilerPlugin),
m_pCompiler(compiler),
m_pProject(project),
m_pCurrTarget(0)
{
//ctor
if (!m_pProject)
return; // probably a compile file cmd without a project
depsStart();
wxFileName cwd;
cwd.Assign(m_pProject->GetBasePath());
depsSetCWD(cwd.GetPath(wxPATH_GET_VOLUME).mb_str());
wxFileName fname(m_pProject->GetFilename());
fname.SetExt(_T("depend"));
depsCacheRead(fname.GetFullPath().mb_str());
}
DirectCommands::~DirectCommands()
{
//dtor
if (!m_pProject)
return; // probably a compile file cmd without a project
struct depsStats stats;
depsGetStats(&stats);
if (stats.cache_updated)
{
wxFileName fname(m_pProject->GetFilename());
fname.SetExt(_T("depend"));
depsCacheWrite(fname.GetFullPath().mb_str());
}
Manager::Get()->GetMessageManager()->DebugLog(
_("Scanned %d files for #includes, cache used %d, cache updated %d"),
stats.scanned, stats.cache_used, stats.cache_updated);
depsDone();
}
// static
void DirectCommands::QuoteStringIfNeeded(wxString& str)
{
if (!str.IsEmpty() && str.Find(_T(' ')) != -1 && str.GetChar(0) != _T('"'))
str = wxString(_T("\"")) + str + _T("\"");
}
// static
void DirectCommands::AppendArray(const wxArrayString& from, wxArrayString& to)
{
for (unsigned int i = 0; i < from.GetCount(); ++i)
{
to.Add(from[i]);
}
}
void DirectCommands::AddCommandsToArray(const wxString& cmds, wxArrayString& array)
{
wxString cmd = cmds;
while (!cmd.IsEmpty())
{
int idx = cmd.Find(_T("\n"));
wxString cmdpart = idx != -1 ? cmd.Left(idx) : cmd;
cmdpart.Trim(false);
cmdpart.Trim(true);
if (!cmdpart.IsEmpty())
array.Add(cmdpart);
if (idx == -1)
break;
cmd.Remove(0, idx + 1);
}
}
int MySortProjectFilesByWeight(ProjectFile** one, ProjectFile** two)
{
return (*one)->weight - (*two)->weight;
}
MyFilesArray DirectCommands::GetProjectFilesSortedByWeight(ProjectBuildTarget* target, bool compile, bool link)
{
MyFilesArray files;
for (int i = 0; i < m_pProject->GetFilesCount(); ++i)
{
ProjectFile* pf = m_pProject->GetFile(i);
// require compile
if (compile && !pf->compile)
continue;
// require link
if (link && !pf->link)
continue;
// if the file does not belong in this target (if we have a target), skip it
if (target && (pf->buildTargets.Index(target->GetTitle()) == wxNOT_FOUND))
continue;
files.Add(pf);
}
files.Sort(MySortProjectFilesByWeight);
return files;
}
wxArrayString DirectCommands::CompileFile(ProjectBuildTarget* target, ProjectFile* pf, bool force)
{
wxArrayString ret;
// is it compilable?
if (!pf->compile || pf->compilerVar.IsEmpty())
return ret;
if (!force)
{
DepsSearchStart(target);
pfDetails pfd(this, target, pf);
if (!IsObjectOutdated(pfd))
return ret;
}
if (target)
ret.Add(wxString(COMPILER_TARGET_CHANGE) + target->GetTitle());
AppendArray(GetCompileFileCommand(target, pf), ret);
return ret;
}
wxArrayString DirectCommands::GetCompileFileCommand(ProjectBuildTarget* target, ProjectFile* pf)
{
wxLogNull ln;
wxArrayString ret;
// is it compilable?
if (!pf->compile || pf->compilerVar.IsEmpty())
return ret;
pfDetails pfd(this, target, pf);
MakefileGenerator mg(m_pCompilerPlugin, m_pProject, _T(""), 0); // don't worry! we just need a couple of utility funcs from it
// lookup file's type
FileType ft = FileTypeOf(pf->relativeFilename);
// create output dir
if (!pfd.object_dir_native.IsEmpty() && !wxDirExists(pfd.object_dir_native))
{
if (!CreateDirRecursively(pfd.object_dir_native, 0755))
wxMessageBox(_("Can't create object output directory ") + pfd.object_dir_native);
}
bool isResource = ft == ftResource;
bool isHeader = ft == ftHeader;
#ifndef __WXMSW__
// not supported under non-win32 platforms
if (isResource)
return ret;
#endif
Compiler* compiler = target ? CompilerFactory::Compilers[target->GetCompilerIndex()] : m_pCompiler;
wxString compilerCmd = mg.CreateSingleFileCompileCmd(pf->useCustomBuildCommand
? pf->buildCommand
: compiler->GetCommand(isResource ? ctCompileResourceCmd : ctCompileObjectCmd),
target,
pf,
pfd.source_file,
pfd.object_file,
pfd.dep_file);
if (!compilerCmd.IsEmpty())
{
switch (compiler->GetSwitches().logging)
{
case clogFull:
ret.Add(wxString(COMPILER_SIMPLE_LOG) + compilerCmd);
break;
case clogSimple:
if (isHeader)
ret.Add(wxString(COMPILER_SIMPLE_LOG) + _("Precompiling header: ") + pfd.source_file_native);
else
ret.Add(wxString(COMPILER_SIMPLE_LOG) + _("Compiling: ") + pfd.source_file_native);
break;
default:
break;
}
AddCommandsToArray(compilerCmd, ret);
}
return ret;
}
/// This is to be used *only* for files not belonging to a project!!!
wxArrayString DirectCommands::GetCompileSingleFileCommand(const wxString& filename)
{
wxLogNull ln;
wxArrayString ret;
// lookup file's type
FileType ft = FileTypeOf(filename);
// is it compilable?
if (ft != ftSource)
return ret;
wxFileName fname(filename);
fname.SetExt(m_pCompiler->GetSwitches().objectExtension);
wxString o_filename = fname.GetFullPath();
fname.SetExt(EXECUTABLE_EXT);
wxString exe_filename = fname.GetFullPath();
wxString s_filename = filename;
QuoteStringIfNeeded(s_filename);
QuoteStringIfNeeded(o_filename);
MakefileGenerator mg(m_pCompilerPlugin, 0, _T(""), 0); // don't worry! we just need a couple of utility funcs from it
wxString compilerCmd = mg.CreateSingleFileCompileCmd(ctCompileObjectCmd,
0,
0,
s_filename,
o_filename,
wxEmptyString);
wxString linkerCmd = mg.CreateSingleFileCompileCmd(ctLinkConsoleExeCmd,
0,
0,
wxEmptyString,
o_filename,
wxEmptyString);
if (!compilerCmd.IsEmpty())
{
switch (m_pCompiler->GetSwitches().logging)
{
case clogFull:
ret.Add(wxString(COMPILER_SIMPLE_LOG) + compilerCmd);
break;
case clogSimple:
ret.Add(wxString(COMPILER_SIMPLE_LOG) + _("Compiling: ") + filename);
break;
default:
break;
}
AddCommandsToArray(compilerCmd, ret);
}
if (!linkerCmd.IsEmpty())
{
switch (m_pCompiler->GetSwitches().logging)
{
case clogFull:
ret.Add(wxString(COMPILER_SIMPLE_LOG) + linkerCmd);
break;
default:
ret.Add(wxString(COMPILER_SIMPLE_LOG) + _("Linking console executable: ") + exe_filename);
break;
}
AddCommandsToArray(linkerCmd, ret);
}
return ret;
}
wxArrayString DirectCommands::GetCompileCommands(ProjectBuildTarget* target, bool force)
{
wxArrayString ret;
if (target)
ret = GetTargetCompileCommands(target, force);
else
{
// add pre-build commands
AppendArray(GetPreBuildCommands(0L), ret);
size_t counter = ret.GetCount();
for (int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x)
{
ProjectBuildTarget* bt = m_pProject->GetBuildTarget(x);
if (bt->GetIncludeInTargetAll()) // only if target gets build with "all"
{
wxArrayString targetcompile = GetTargetCompileCommands(bt, force);
AppendArray(targetcompile, ret);
}
}
bool needPost = ret.GetCount() != counter;
// remove pre-build commands if no compile needed and not always run pre-build commands
if (!needPost && !m_pProject->GetAlwaysRunPreBuildSteps())
ret.Clear();
// add post-build commands
if (needPost || m_pProject->GetAlwaysRunPostBuildSteps())
AppendArray(GetPostBuildCommands(0L), ret);
}
return ret;
}
wxArrayString DirectCommands::GetTargetCompileCommands(ProjectBuildTarget* target, bool force)
{
wxArrayString ret;
ret.Add(wxString(COMPILER_SIMPLE_LOG) + _("Switching to target: ") + target->GetTitle());
// NOTE: added this to notify compiler about the active target.
// this is needed when targets use different compiler each
// and C::B tries to parse the compiler's output.
// previous behaviour, used the project's compiler for parsing
// all targets output, which failed when a target's compiler
// was different than the project's...
ret.Add(wxString(COMPILER_TARGET_CHANGE) + target->GetTitle());
m_pCurrTarget = target;
// make sure all project files are saved
if (!m_pProject->SaveAllFiles())
wxMessageBox(_("Could not save all files. Build might be incomplete..."));
// add pre-build commands
AppendArray(GetPreBuildCommands(target), ret);
if (target->GetTargetType() == ttCommandsOnly)
{
// commands-only target
// we just have to run the post-build step
// runs *only* when:
// 1) "always run post-build step" is checked
// or
// 2) we have specified additional output files and the external-dep check fails
wxString added = target->GetAdditionalOutputFiles();
if (target->GetAlwaysRunPostBuildSteps() || // or always run post-build steps
(!added.IsEmpty() && // additional output files assigned
AreExternalDepsOutdated(wxEmptyString, added, target->GetExternalDeps()))) // or external dependencies say relink
{
AppendArray(GetPostBuildCommands(target), ret);
}
return ret;
}
// set list of #include directories
DepsSearchStart(target);
// iterate all files of the project/target and add them to the build process
size_t counter = ret.GetCount();
MyFilesArray files = GetProjectFilesSortedByWeight(target, true, false);
for (unsigned int i = 0; i < files.GetCount(); ++i)
{
ProjectFile* pf = files[i];
pfDetails pfd(this, target, pf);
bool doBuild = false;
if (pf->autoDeps)
doBuild = force || IsObjectOutdated(pfd);
else
{
wxString msg;
msg.Printf(_("File %s has custom dependencies set."
"This feature only works when using GNU \"make\""
"for the build process..."), pfd.source_file_native.c_str());
ret.Add(wxString(COMPILER_SIMPLE_LOG) + msg);
}
if (doBuild)
{
// compile file
wxArrayString filecmd = GetCompileFileCommand(target, pf);
AppendArray(filecmd, ret);
}
}
// add link command
wxArrayString link = GetLinkCommands(target, ret.GetCount() != counter);
AppendArray(link, ret);
bool needPost = ret.GetCount() != counter;
// remove pre-build commands if no compile needed and not always run pre-build commands
if (ret.GetCount() == counter && !target->GetAlwaysRunPreBuildSteps())
ret.Clear();
// add post-build commands
if (needPost || target->GetAlwaysRunPostBuildSteps())
AppendArray(GetPostBuildCommands(target), ret);
return ret;
}
wxArrayString DirectCommands::GetPreBuildCommands(ProjectBuildTarget* target)
{
wxArrayString buildcmds = target ? target->GetCommandsBeforeBuild() : m_pProject->GetCommandsBeforeBuild();
if (!buildcmds.IsEmpty())
{
Compiler* compiler = target ? CompilerFactory::Compilers[target->GetCompilerIndex()] : m_pCompiler;
wxString title = target ? target->GetTitle() : m_pProject->GetTitle();
switch (compiler->GetSwitches().logging)
{
case clogFull:
{
wxArrayString tmp;
for (size_t i = 0; i < buildcmds.GetCount(); ++i)
{
wxArrayString tmp2;
AddCommandsToArray(buildcmds[i], tmp2);
for (size_t n = 0; n < tmp2.GetCount(); ++n)
{
tmp.Add(wxString(COMPILER_SIMPLE_LOG) + tmp2[n]);
tmp.Add(tmp2[n]);
}
}
buildcmds = tmp;
}
break;
case clogSimple:
buildcmds.Insert(wxString(COMPILER_SIMPLE_LOG) + _("Running pre-build step: ") + title, 0);
default:
break;
}
}
return buildcmds;
}
wxArrayString DirectCommands::GetPostBuildCommands(ProjectBuildTarget* target)
{
wxArrayString buildcmds = target ? target->GetCommandsAfterBuild() : m_pProject->GetCommandsAfterBuild();
if (!buildcmds.IsEmpty())
{
wxString title = target ? target->GetTitle() : m_pProject->GetTitle();
switch (m_pCompiler->GetSwitches().logging)
{
case clogFull:
{
wxArrayString tmp;
for (size_t i = 0; i < buildcmds.GetCount(); ++i)
{
wxArrayString tmp2;
AddCommandsToArray(buildcmds[i], tmp2);
for (size_t n = 0; n < tmp2.GetCount(); ++n)
{
tmp.Add(wxString(COMPILER_SIMPLE_LOG) + tmp2[n]);
tmp.Add(tmp2[n]);
}
}
buildcmds = tmp;
}
break;
case clogSimple:
buildcmds.Insert(wxString(COMPILER_SIMPLE_LOG) + _("Running post-build step: ") + title, 0);
default:
break;
}
}
return buildcmds;
}
wxArrayString DirectCommands::GetLinkCommands(ProjectBuildTarget* target, bool force)
{
wxArrayString ret;
if (target)
ret = GetTargetLinkCommands(target, force);
else
{
for (int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x)
{
ProjectBuildTarget* bt = m_pProject->GetBuildTarget(x);
if (bt->GetIncludeInTargetAll()) // only if target gets build with "all"
{
wxArrayString targetlink = GetTargetLinkCommands(bt, force);
AppendArray(targetlink, ret);
}
}
}
return ret;
}
wxArrayString DirectCommands::GetTargetLinkCommands(ProjectBuildTarget* target, bool force)
{
wxLogNull ln;
wxArrayString ret;
MakefileGenerator mg(m_pCompilerPlugin, m_pProject, _T(""), 0); // don't worry! we just need a couple of utility funcs from it
wxFileName out = UnixFilename(target->GetOutputFilename());
wxString output = target->GetOutputFilename();
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(output);
wxString linkfiles;
wxString resfiles;
time_t outputtime;
depsTimeStamp(output.mb_str(), &outputtime);
if (!outputtime)
force = true;
if (AreExternalDepsOutdated(out.GetFullPath(), target->GetAdditionalOutputFiles(), target->GetExternalDeps()))
force = true;
wxString prependHack; // part of the following hack
if (target->GetTargetType() == ttStaticLib)
{
// QUICK HACK: some linkers (e.g. bcc, dmc) require a - or + in front of
// object files for static library. What we 'll do here until we redesign
// the thing, is to accept this symbol as part of the $link_objects macro
// like this:
// $+link_objects
// $-link_objects
// $-+link_objects
// $+-link_objects
//
// So, we first scan the command for this special case and, if found,
// set a flag so that the linkfiles array is filled with the correct options
Compiler* compiler = target ? CompilerFactory::Compilers[target->GetCompilerIndex()] : m_pCompiler;
wxString compilerCmd = compiler->GetCommand(ctLinkStaticCmd);
wxRegEx re(_T("\\$([-+]+)link_objects"));
if (re.Matches(compilerCmd))
prependHack = re.GetMatch(compilerCmd, 1);
}
// get all the linkable objects for the target
MyFilesArray files = GetProjectFilesSortedByWeight(target, false, true);
for (unsigned int i = 0; i < files.GetCount(); ++i)
{
ProjectFile* pf = files[i];
pfDetails pfd(this, target, pf);
if (FileTypeOf(pf->relativeFilename) == ftResource)
resfiles << pfd.object_file << _T(" ");
else
linkfiles << prependHack << pfd.object_file << _T(" "); // see QUICK HACK above (prependHack)
// timestamp check
if (!force)
{
time_t objtime;
depsTimeStamp(pfd.object_file_native.mb_str(), &objtime);
if (objtime > outputtime)
force = true;
}
}
if (!force)
return ret;
// create output dir
out.MakeAbsolute(m_pProject->GetBasePath());
wxString dstname = out.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(dstname);
if (!dstname.IsEmpty() && !wxDirExists(dstname))
{
if (!CreateDirRecursively(dstname, 0755))
wxMessageBox(_("Can't create output directory ") + dstname);
}
// add actual link command
wxString kind_of_output;
CommandType ct;
switch (target->GetTargetType())
{
case ttConsoleOnly:
ct = ctLinkConsoleExeCmd;
kind_of_output = _("console executable");
break;
case ttExecutable:
ct = ctLinkExeCmd;
kind_of_output = _("executable");
break;
case ttDynamicLib:
ct = ctLinkDynamicCmd;
kind_of_output = _("dynamic library");
break;
case ttStaticLib:
ct = ctLinkStaticCmd;
kind_of_output = _("static library");
break;
default: break;
}
wxString compilerCmd = mg.CreateSingleFileCompileCmd(ct, target, 0, _T(""), linkfiles, resfiles);
if (!compilerCmd.IsEmpty())
{
Compiler* compiler = target ? CompilerFactory::Compilers[target->GetCompilerIndex()] : m_pCompiler;
switch (compiler->GetSwitches().logging)
{
case clogFull:
ret.Add(wxString(COMPILER_SIMPLE_LOG) + compilerCmd);
break;
default: // linker always simple log (if not full)
ret.Add(wxString(COMPILER_SIMPLE_LOG) + _("Linking ") + kind_of_output + _T(": ") + target->GetOutputFilename());
break;
}
// for an explanation of the following, see GetTargetCompileCommands()
if (target && ret.GetCount() != 0)
ret.Add(wxString(COMPILER_TARGET_CHANGE) + target->GetTitle());
AddCommandsToArray(compilerCmd, ret);
}
return ret;
}
wxArrayString DirectCommands::GetCleanCommands(ProjectBuildTarget* target, bool distclean)
{
wxArrayString ret;
if (target)
ret = GetTargetCleanCommands(target);
else
{
for (int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x)
{
ProjectBuildTarget* bt = m_pProject->GetBuildTarget(x);
wxArrayString targetclear = GetTargetCleanCommands(bt, distclean);
AppendArray(targetclear, ret);
}
}
return ret;
}
wxArrayString DirectCommands::GetTargetCleanCommands(ProjectBuildTarget* target, bool distclean)
{
wxArrayString ret;
// add object files
MyFilesArray files = GetProjectFilesSortedByWeight(target, true, false);
for (unsigned int i = 0; i < files.GetCount(); ++i)
{
ProjectFile* pf = files[i];
pfDetails pfd(this, target, pf);
ret.Add(pfd.object_file_absolute_native);
if (distclean)
ret.Add(pfd.dep_file_absolute_native);
}
// add target output
wxString outputfilename = target->GetOutputFilename();
if (target->GetTargetType() != ttCommandsOnly)
{
ret.Add(outputfilename);
}
if (target->GetTargetType() == ttDynamicLib)
{
// for dynamic libs, delete static lib
ret.Add(target->GetStaticLibFilename());
// .def exports file is not deleted, because it may be user-supplied
// ret.Add(target->GetDynamicLibDefFilename());
}
return ret;
}
/** external deps are manualy set by the user
* e.g. a static library linked to the project is an external dep (if set as such by the user)
* so that a re-linking is forced if the static lib is updated
*/
bool DirectCommands::AreExternalDepsOutdated(const wxString& buildOutput, const wxString& additionalFiles, const wxString& externalDeps)
{
// array is separated by ;
wxArrayString deps = GetArrayFromString(externalDeps, _T(";"));
wxArrayString files = GetArrayFromString(additionalFiles, _T(";"));
for (size_t i = 0; i < deps.GetCount(); ++i)
{
if (deps[i].IsEmpty())
continue;
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(deps[i]);
time_t timeSrc;
depsTimeStamp(deps[i].mb_str(), &timeSrc);
// if external dep doesn't exist, no need to relink
if (!timeSrc)
return false;
// let's check the additional output files
for (size_t x = 0; x < files.GetCount(); ++x)
{
if (files[i].IsEmpty())
continue;
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(files[i]);
time_t addT;
depsTimeStamp(files[i].mb_str(), &addT);
// if additional file doesn't exist, we can skip it
if (!addT)
continue;
// if external dep is newer than additional file, relink
if (timeSrc > addT)
return true;
}
// if no output, probably a commands-only target; nothing to relink
if (buildOutput.IsEmpty())
return false;
// now check the target's output
// this is moved last because, for "commands only" targets,
// it would return before we had a chance to check the
// additional output files (above)
wxString output = buildOutput;
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(output);
time_t timeExe;
depsTimeStamp(output.mb_str(), &timeExe);
// if build output doesn't exist, relink
if (!timeExe)
return true;
// if external dep is newer than build output, relink
if (timeSrc > timeExe)
return true;
}
return false; // no force relink
}
bool DirectCommands::IsObjectOutdated(const pfDetails& pfd)
{
// If the source file does not exist, then do not compile.
time_t timeSrc;
depsTimeStamp(pfd.source_file_absolute_native.mb_str(), &timeSrc);
if (!timeSrc)
return false;
// If the object file does not exist, then it must be built. In this case
// there is no need to scan the source file for headers.
time_t timeObj;
depsTimeStamp(pfd.object_file_absolute_native.mb_str(), &timeObj);
if (!timeObj)
return true;
// If the source file is newer than the object file, then the object file
// must be built. In this case there is no need to scan the source file
// for headers.
if (timeSrc > timeObj)
return true;
// Scan the source file for headers. Result is NULL if the file does
// not exist. If one of the descendent header files is newer than the
// object file, then the object file must be built.
depsRef ref = depsScanForHeaders(pfd.source_file_absolute_native.mb_str());
if (ref)
{
time_t timeNewest;
(void) depsGetNewest(ref, &timeNewest);
return (timeNewest > timeObj);
}
// Source file doesn't exist.
return false;
}
void DirectCommands::DepsSearchStart(ProjectBuildTarget* target)
{
depsSearchStart();
wxArrayString prj_incs = m_pProject->GetIncludeDirs();
wxArrayString tgt_incs = target->GetIncludeDirs();
// replace custom vars in include dirs
for (unsigned int i = 0; i < prj_incs.GetCount(); ++i)
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(prj_incs[i]);
for (unsigned int i = 0; i < tgt_incs.GetCount(); ++i)
Manager::Get()->GetMacrosManager()->ReplaceEnvVars(tgt_incs[i]);
OptionsRelation relation = target->GetOptionRelation(ortIncludeDirs);
switch (relation)
{
case orUseParentOptionsOnly:
for (unsigned int i = 0; i < prj_incs.GetCount(); ++i)
depsAddSearchDir(prj_incs[i].mb_str());
break;
case orUseTargetOptionsOnly:
for (unsigned int i = 0; i < tgt_incs.GetCount(); ++i)
depsAddSearchDir(tgt_incs[i].mb_str());
break;
case orPrependToParentOptions:
for (unsigned int i = 0; i < tgt_incs.GetCount(); ++i)
depsAddSearchDir(tgt_incs[i].mb_str());
for (unsigned int i = 0; i < prj_incs.GetCount(); ++i)
depsAddSearchDir(prj_incs[i].mb_str());
break;
case orAppendToParentOptions:
for (unsigned int i = 0; i < prj_incs.GetCount(); ++i)
depsAddSearchDir(prj_incs[i].mb_str());
for (unsigned int i = 0; i < tgt_incs.GetCount(); ++i)
depsAddSearchDir(tgt_incs[i].mb_str());
break;
}
// We could add the "global" compiler directories too, but we normally
// don't care about the modification times of system include files.
}
syntax highlighted by Code2HTML, v. 0.9.1