/*
* This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
* http://www.gnu.org/licenses/lgpl-3.0.html
*
* $Revision$
* $Id$
* $HeadURL$
*/
#include "sdk_precomp.h"
#ifndef CB_PRECOMP
#include <wx/confbase.h>
#include <wx/fileconf.h>
#include <wx/intl.h>
#include <wx/filename.h>
#include <wx/msgdlg.h>
#include <wx/stopwatch.h>
#include "manager.h"
#include "configmanager.h"
#include "projectmanager.h"
#include "logmanager.h"
#include "macrosmanager.h"
#include "cbproject.h"
#include "compilerfactory.h"
#include "globals.h"
#endif
#include <wx/dir.h>
#include <string>
#include <algorithm>
#include "filefilters.h"
#include "projectloader.h"
#include "projectloader_hooks.h"
#include "annoyingdialog.h"
#include "configmanager.h"
#include "tinywxuni.h"
#include "filegroupsandmasks.h"
ProjectLoader::ProjectLoader(cbProject* project)
: m_pProject(project),
m_Upgraded(false),
m_OpenDirty(false),
m_1_4_to_1_5_deftarget(-1),
m_IsPre_1_6(false)
{
//ctor
}
ProjectLoader::~ProjectLoader()
{
//dtor
}
bool ProjectLoader::Open(const wxString& filename)
{
return Open(filename, nullptr);
}
bool ProjectLoader::Open(const wxString& filename, TiXmlElement** ppExtensions)
{
LogManager* pMsg = Manager::Get()->GetLogManager();
if (!pMsg)
return false;
wxStopWatch sw;
pMsg->DebugLog("Loading project file...");
TiXmlDocument doc;
if (!TinyXML::LoadDocument(filename, &doc))
return false;
pMsg->DebugLog("Parsing project file...");
TiXmlElement* root;
TiXmlElement* proj;
root = doc.FirstChildElement("CodeBlocks_project_file");
if (!root)
{
// old tag
root = doc.FirstChildElement("Code::Blocks_project_file");
if (!root)
{
pMsg->DebugLog("Not a valid Code::Blocks project file...");
return false;
}
}
proj = root->FirstChildElement("Project");
if (!proj)
{
pMsg->DebugLog("No 'Project' element in file...");
return false;
}
m_IsPre_1_2 = false; // flag for some changed defaults in version 1.2
TiXmlElement* version = root->FirstChildElement("FileVersion");
// don't show messages if we 're running a batch build (i.e. no gui)
if (!Manager::IsBatchBuild() && version)
{
int major = PROJECT_FILE_VERSION_MAJOR;
int minor = PROJECT_FILE_VERSION_MINOR;
version->QueryIntAttribute("major", &major);
version->QueryIntAttribute("minor", &minor);
m_IsPre_1_6 = major < 1 || (major == 1 && minor < 6);
if (major < 1 ||
(major == 1 && minor < 2))
{
// pre-1.2
pMsg->DebugLog(wxString::Format("Project version is %d.%d. Defaults have changed since then...", major, minor));
m_IsPre_1_2 = true;
}
else if (major >= PROJECT_FILE_VERSION_MAJOR && minor > PROJECT_FILE_VERSION_MINOR)
{
pMsg->DebugLog(wxString::Format("Project version is > %d.%d. Trying to load...", PROJECT_FILE_VERSION_MAJOR, PROJECT_FILE_VERSION_MINOR));
AnnoyingDialog dlg(_("Project file format is newer/unknown"),
_("This project file was saved with a newer version of Code::Blocks.\n"
"Will try to load, but you should make sure all the settings were loaded correctly..."),
wxART_WARNING,
AnnoyingDialog::OK);
dlg.ShowModal();
}
else
{
// use one message for all changes
wxString msg;
wxString warn_msg;
// 1.5 -> 1.6: values matching defaults are not written to <Unit> sections
if (major == 1 && minor == 5)
{
msg << _("1.5 to 1.6:\n"
" * only saves values that differ from defaults (i.e. project files are smaller now).\n"
" * added object names generation mode setting (normal/extended).\n"
" * added project notes.\n");
msg << '\n';
warn_msg << _("* Project file updated to version 1.6:\n"
" When a project file is saved as version 1.6, it will NO LONGER be read correctly\n"
" by earlier Code::Blocks versions!\n"
" So, if you plan on using this project with an earlier Code::Blocks version, you\n"
" should probably NOT save this project as version 1.6...\n");
warn_msg << '\n';
}
// 1.4 -> 1.5: updated custom build command per-project file
if (major == 1 && minor == 4)
{
msg << _("1.4 to 1.5:\n"
" * added virtual build targets.\n");
msg << '\n';
}
// 1.3 -> 1.4: updated custom build command per-project file
if (major == 1 && minor == 3)
{
msg << _("1.3 to 1.4:\n"
" * changed the way custom file build commands are stored (no auto-conversion).\n");
msg << '\n';
}
if (!msg.IsEmpty())
{
m_Upgraded = true;
msg.Prepend(wxString::Format(_("Project file format is older (%d.%d) than the current format (%d.%d).\n"
"The file will automatically be upgraded on save.\n"
"But please read the following list of changes, as some of them "
"might not automatically convert existing (old) settings.\n"
"If you don't understand what a change means, you probably don't "
"use that feature so you don't have to worry about it.\n\n"
"List of changes:\n"),
major,
minor,
PROJECT_FILE_VERSION_MAJOR,
PROJECT_FILE_VERSION_MINOR));
AnnoyingDialog dlg(_("Project file format changed"),
msg,
wxART_INFORMATION,
AnnoyingDialog::OK);
dlg.ShowModal();
}
if (!warn_msg.IsEmpty())
{
warn_msg.Prepend(_("!!! WARNING !!!\n\n"));
AnnoyingDialog dlg(_("Project file upgrade warning"),
warn_msg,
wxART_WARNING,
AnnoyingDialog::OK);
dlg.ShowModal();
}
}
}
DoProjectOptions(proj);
DoBuild(proj);
DoCompilerOptions(proj);
DoResourceCompilerOptions(proj);
DoLinkerOptions(proj);
DoIncludesOptions(proj);
DoLibsOptions(proj);
DoExtraCommands(proj);
DoUnits(proj);
// if targets still use the "build with all" flag,
// it's time for conversion
if (!m_pProject->HasVirtualBuildTarget("All"))
{
wxArrayString all;
for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
{
ProjectBuildTarget* bt = m_pProject->GetBuildTarget(i);
if (bt && bt->GetIncludeInTargetAll())
all.Add(bt->GetTitle());
}
if (all.GetCount())
{
m_pProject->DefineVirtualBuildTarget("All", all);
m_Upgraded = true;
}
}
// convert old deftarget int to string
if (m_1_4_to_1_5_deftarget != -1)
{
ProjectBuildTarget* bt = m_pProject->GetBuildTarget(m_1_4_to_1_5_deftarget);
if (bt)
m_pProject->SetDefaultExecuteTarget(bt->GetTitle());
}
if (ppExtensions)
*ppExtensions = nullptr;
// as a last step, run all hooked callbacks
TiXmlElement* node = proj->FirstChildElement("Extensions");
if (node)
{
if (ppExtensions)
*ppExtensions = new TiXmlElement(*node);
ProjectLoaderHooks::CallHooks(m_pProject, node, true);
}
if (!version)
{
// pre 1.1 version
ConvertVersion_Pre_1_1();
// format changed also:
// removed <IncludeDirs> and <LibDirs> elements and added them as child elements
// in <Compiler> and <Linker> elements respectively
// so set m_Upgraded to true, irrespectively of libs detection...
m_Upgraded = true;
}
else
{
// do something important based on version
// wxString major = version->Attribute("major");
// wxString minor = version->Attribute("minor");
}
pMsg->DebugLog(wxString::Format("Done loading project in %ld ms", sw.Time()));
return true;
}
void ProjectLoader::ConvertVersion_Pre_1_1()
{
// ask to detect linker libraries and move them to the new
// CompileOptionsBase linker libs container
wxString msg;
msg.Printf(_("Project \"%s\" was saved with an earlier version of Code::Blocks.\n"
"In the current version, link libraries are treated separately from linker options.\n"
"Do you want to auto-detect the libraries \"%s\" is using and configure it accordingly?"),
m_pProject->GetTitle(), m_pProject->GetTitle());
if (cbMessageBox(msg, _("Question"), wxICON_QUESTION | wxYES_NO) == wxID_YES)
{
// project first
ConvertLibraries(m_pProject);
for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
{
ConvertLibraries(m_pProject->GetBuildTarget(i));
m_Upgraded = true;
}
}
}
void ProjectLoader::ConvertLibraries(CompileTargetBase* object)
{
wxArrayString linkerOpts = object->GetLinkerOptions();
wxArrayString linkLibs = object->GetLinkLibs();
wxString compilerId = object->GetCompilerID();
Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
if (!compiler)
return;
wxString linkLib = compiler->GetSwitches().linkLibs;
wxString libExt = compiler->GetSwitches().libExtension;
size_t libExtLen = libExt.Length();
size_t i = 0;
while (i < linkerOpts.GetCount())
{
wxString opt = linkerOpts[i];
if (!linkLib.IsEmpty() && opt.StartsWith(linkLib))
{
opt.Remove(0, 2);
wxString ext = compiler->GetSwitches().libExtension;
if (!ext.IsEmpty())
ext = "." + ext;
linkLibs.Add(compiler->GetSwitches().libPrefix + opt + ext);
linkerOpts.RemoveAt(i, 1);
}
else if (opt.Length() > libExtLen && opt.Right(libExtLen) == libExt)
{
linkLibs.Add(opt);
linkerOpts.RemoveAt(i, 1);
}
else
++i;
}
object->SetLinkerOptions(linkerOpts);
object->SetLinkLibs(linkLibs);
}
void ProjectLoader::DoMakeCommands(TiXmlElement* parentNode, CompileTargetBase* target)
{
if (!parentNode)
return; // no options
TiXmlElement* node;
node = parentNode->FirstChildElement("Build");
if (node && node->Attribute("command"))
target->SetMakeCommandFor(mcBuild, cbC2U(node->Attribute("command")));
node = parentNode->FirstChildElement("CompileFile");
if (node && node->Attribute("command"))
target->SetMakeCommandFor(mcCompileFile, cbC2U(node->Attribute("command")));
node = parentNode->FirstChildElement("Clean");
if (node && node->Attribute("command"))
target->SetMakeCommandFor(mcClean, cbC2U(node->Attribute("command")));
node = parentNode->FirstChildElement("DistClean");
if (node && node->Attribute("command"))
target->SetMakeCommandFor(mcDistClean, cbC2U(node->Attribute("command")));
node = parentNode->FirstChildElement("AskRebuildNeeded");
if (node && node->Attribute("command"))
target->SetMakeCommandFor(mcAskRebuildNeeded, cbC2U(node->Attribute("command")));
node = parentNode->FirstChildElement("SilentBuild");
if (node && node->Attribute("command"))
target->SetMakeCommandFor(mcSilentBuild, cbC2U(node->Attribute("command")));
}
void ProjectLoader::DoVirtualTargets(TiXmlElement* parentNode)
{
if (!parentNode)
return;
TiXmlElement* node = parentNode->FirstChildElement("Add");
if (!node)
return; // no virtual targets
while (node)
{
if (node->Attribute("alias") && node->Attribute("targets"))
{
wxString alias = cbC2U(node->Attribute("alias"));
wxString targets = cbC2U(node->Attribute("targets"));
wxArrayString arr = GetArrayFromString(targets, ";", true);
m_pProject->DefineVirtualBuildTarget(alias, arr);
}
node = node->NextSiblingElement("Add");
}
}
void ProjectLoader::DoProjectOptions(TiXmlElement* parentNode)
{
TiXmlElement* node = parentNode->FirstChildElement("Option");
if (!node)
return; // no options
wxString title;
wxString makefile;
bool makefile_custom = false;
wxString execution_dir;
wxString defaultTarget;
wxString compilerId = "gcc";
bool extendedObjectNames = false;
wxArrayString vfolders;
int platformsFinal = spAll;
PCHMode pch_mode = m_IsPre_1_2 ? pchSourceDir : pchObjectDir;
bool showNotes = false;
bool checkFiles = true;
wxString notes;
// loop through all options
while (node)
{
if (node->Attribute("title"))
{
title = cbC2U(node->Attribute("title"));
if (title.Trim().IsEmpty())
title = "untitled";
}
else if (node->Attribute("platforms"))
platformsFinal = GetPlatformsFromString(cbC2U(node->Attribute("platforms")));
else if (node->Attribute("makefile")) // there is only one attribute per option, so "else" is a safe optimisation
makefile = UnixFilename(cbC2U(node->Attribute("makefile")));
else if (node->Attribute("makefile_is_custom"))
makefile_custom = strncmp(node->Attribute("makefile_is_custom"), "1", 1) == 0;
else if (node->Attribute("execution_dir"))
execution_dir = UnixFilename(cbC2U(node->Attribute("execution_dir")));
// old default_target (int) node
else if (node->QueryIntAttribute("default_target", &m_1_4_to_1_5_deftarget) == TIXML_SUCCESS)
{
// we read the value
}
else if (node->Attribute("default_target"))
defaultTarget = cbC2U(node->Attribute("default_target"));
else if (node->Attribute("compiler"))
compilerId = GetValidCompilerID(cbC2U(node->Attribute("compiler")), "the project");
else if (node->Attribute("extended_obj_names"))
extendedObjectNames = strncmp(node->Attribute("extended_obj_names"), "1", 1) == 0;
else if (node->Attribute("pch_mode"))
pch_mode = (PCHMode)atoi(node->Attribute("pch_mode"));
else if (node->Attribute("virtualFolders"))
vfolders = GetArrayFromString(cbC2U(node->Attribute("virtualFolders")), ";");
else if (node->Attribute("show_notes"))
{
TiXmlHandle parentHandle(node);
TiXmlText* t = (TiXmlText *) parentHandle.FirstChild("notes").FirstChild().Node();
if (t)
notes = cbC2U(t->Value());
showNotes = !notes.IsEmpty() && strncmp(node->Attribute("show_notes"), "1", 1) == 0;
}
else if (node->Attribute("check_files"))
checkFiles = strncmp(node->Attribute("check_files"), "0", 1) != 0;
node = node->NextSiblingElement("Option");
}
m_pProject->SetTitle(title);
m_pProject->SetPlatforms(platformsFinal);
m_pProject->SetMakefile(makefile);
m_pProject->SetMakefileCustom(makefile_custom);
m_pProject->SetMakefileExecutionDir(execution_dir);
m_pProject->SetDefaultExecuteTarget(defaultTarget);
m_pProject->SetCompilerID(compilerId);
m_pProject->SetExtendedObjectNamesGeneration(extendedObjectNames);
m_pProject->SetModeForPCH(pch_mode);
m_pProject->SetVirtualFolders(vfolders);
m_pProject->SetNotes(notes);
m_pProject->SetShowNotesOnLoad(showNotes);
m_pProject->SetCheckForExternallyModifiedFiles(checkFiles);
DoMakeCommands(parentNode->FirstChildElement("MakeCommands"), m_pProject);
DoVirtualTargets(parentNode->FirstChildElement("VirtualTargets"));
}
void ProjectLoader::DoBuild(TiXmlElement* parentNode)
{
TiXmlElement* node = parentNode->FirstChildElement("Build");
while (node)
{
TiXmlElement* opt = node->FirstChildElement("Script");
while (opt)
{
if (opt->Attribute("file"))
m_pProject->AddBuildScript(cbC2U(opt->Attribute("file")));
opt = opt->NextSiblingElement("Script");
}
DoBuildTarget(node);
DoEnvironment(node, m_pProject);
node = node->NextSiblingElement("Build");
}
}
void ProjectLoader::DoBuildTarget(TiXmlElement* parentNode)
{
TiXmlElement* node = parentNode->FirstChildElement("Target");
if (!node)
return; // no options
while (node)
{
ProjectBuildTarget* target = nullptr;
wxString title = cbC2U(node->Attribute("title"));
if (!title.IsEmpty())
target = m_pProject->AddBuildTarget(title);
if (target)
{
Manager::Get()->GetLogManager()->DebugLog("Loading target " + title);
DoBuildTargetOptions(node, target);
DoCompilerOptions(node, target);
DoResourceCompilerOptions(node, target);
DoLinkerOptions(node, target);
DoIncludesOptions(node, target);
DoLibsOptions(node, target);
DoExtraCommands(node, target);
DoEnvironment(node, target);
}
node = node->NextSiblingElement("Target");
}
}
void ProjectLoader::DoBuildTargetOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
TiXmlElement* node = parentNode->FirstChildElement("Option");
if (!node)
return; // no options
bool use_console_runner = true;
wxString output;
wxString imp_lib;
wxString def_file;
wxString working_dir;
wxString obj_output;
wxString deps_output;
wxString deps;
wxString added;
int type = -1;
int platformsFinal = spAll;
wxString compilerId = m_pProject->GetCompilerID();
wxString parameters;
wxString hostApplication;
bool runHostApplicationInTerminal = false;
bool includeInTargetAll = m_IsPre_1_2 ? true : false;
bool createStaticLib = false;
bool createDefFile = false;
int projectCompilerOptionsRelation = 3;
int projectLinkerOptionsRelation = 3;
int projectIncludeDirsRelation = 3;
int projectLibDirsRelation = 3;
int projectResIncludeDirsRelation = 3;
TargetFilenameGenerationPolicy prefixPolicy = tgfpNone; // tgfpNone for compat. with older projects
TargetFilenameGenerationPolicy extensionPolicy = tgfpNone;
while (node)
{
if (node->Attribute("platforms"))
platformsFinal = GetPlatformsFromString(cbC2U(node->Attribute("platforms")));
if (node->Attribute("use_console_runner"))
use_console_runner = strncmp(node->Attribute("use_console_runner"), "0", 1) != 0;
if (node->Attribute("output"))
output = UnixFilename(cbC2U(node->Attribute("output")));
if (node->Attribute("imp_lib"))
imp_lib = UnixFilename(cbC2U(node->Attribute("imp_lib")));
if (node->Attribute("def_file"))
def_file = UnixFilename(cbC2U(node->Attribute("def_file")));
if (node->Attribute("prefix_auto"))
prefixPolicy = atoi(node->Attribute("prefix_auto")) == 1 ? tgfpPlatformDefault : tgfpNone;
if (node->Attribute("extension_auto"))
extensionPolicy = atoi(node->Attribute("extension_auto")) == 1 ? tgfpPlatformDefault : tgfpNone;
if (node->Attribute("working_dir"))
working_dir = UnixFilename(cbC2U(node->Attribute("working_dir")));
if (node->Attribute("object_output"))
obj_output = UnixFilename(cbC2U(node->Attribute("object_output")));
if (node->Attribute("deps_output"))
deps_output = UnixFilename(cbC2U(node->Attribute("deps_output")));
if (node->Attribute("external_deps"))
deps = UnixFilename(cbC2U(node->Attribute("external_deps")));
if (node->Attribute("additional_output"))
added = UnixFilename(cbC2U(node->Attribute("additional_output")));
if (node->Attribute("type"))
type = atoi(node->Attribute("type"));
if (node->Attribute("compiler"))
compilerId = GetValidCompilerID(cbC2U(node->Attribute("compiler")), target->GetTitle());
if (node->Attribute("parameters"))
parameters = cbC2U(node->Attribute("parameters"));
if (node->Attribute("host_application"))
hostApplication = UnixFilename(cbC2U(node->Attribute("host_application")));
if (node->Attribute("run_host_application_in_terminal"))
{
wxString runInTerminal = cbC2U(node->Attribute("run_host_application_in_terminal"));
runHostApplicationInTerminal = (runInTerminal == wxT("1"));
}
// used in versions prior to 1.5
if (node->Attribute("includeInTargetAll"))
includeInTargetAll = atoi(node->Attribute("includeInTargetAll")) != 0;
if (node->Attribute("createDefFile"))
createDefFile = atoi(node->Attribute("createDefFile")) != 0;
if (node->Attribute("createStaticLib"))
createStaticLib = atoi(node->Attribute("createStaticLib")) != 0;
if (node->Attribute("projectCompilerOptionsRelation"))
projectCompilerOptionsRelation = atoi(node->Attribute("projectCompilerOptionsRelation"));
if (node->Attribute("projectLinkerOptionsRelation"))
projectLinkerOptionsRelation = atoi(node->Attribute("projectLinkerOptionsRelation"));
if (node->Attribute("projectIncludeDirsRelation"))
projectIncludeDirsRelation = atoi(node->Attribute("projectIncludeDirsRelation"));
if (node->Attribute("projectLibDirsRelation"))
projectLibDirsRelation = atoi(node->Attribute("projectLibDirsRelation"));
if (node->Attribute("projectResourceIncludeDirsRelation"))
{
projectResIncludeDirsRelation = atoi(node->Attribute("projectResourceIncludeDirsRelation"));
// there used to be a bug in this setting and it might have a negative or very big number
// detect this case and set as default
if (projectResIncludeDirsRelation < 0 || projectResIncludeDirsRelation >= ortLast)
projectResIncludeDirsRelation = 3;
}
node = node->NextSiblingElement("Option");
}
node = parentNode->FirstChildElement("Script");
while (node)
{
if (node->Attribute("file"))
target->AddBuildScript(cbC2U(node->Attribute("file")));
node = node->NextSiblingElement("Script");
}
if (type != -1)
{
target->SetPlatforms(platformsFinal);
target->SetCompilerID(compilerId);
target->SetTargetFilenameGenerationPolicy(prefixPolicy, extensionPolicy);
target->SetTargetType((TargetType)type); // type *must* come before output filename!
target->SetOutputFilename(output); // because if no filename defined, one will be suggested based on target type...
target->SetImportLibraryFilename(imp_lib);
target->SetDefinitionFileFilename(def_file);
target->SetUseConsoleRunner(use_console_runner);
if (!working_dir.IsEmpty())
target->SetWorkingDir(working_dir);
if (!obj_output.IsEmpty())
target->SetObjectOutput(obj_output);
if (!deps_output.IsEmpty())
target->SetDepsOutput(deps_output);
target->SetExternalDeps(deps);
target->SetAdditionalOutputFiles(added);
target->SetExecutionParameters(parameters);
target->SetHostApplication(hostApplication);
target->SetRunHostApplicationInTerminal(runHostApplicationInTerminal);
target->SetIncludeInTargetAll(includeInTargetAll); // used in versions prior to 1.5
target->SetCreateDefFile(createDefFile);
target->SetCreateStaticLib(createStaticLib);
target->SetOptionRelation(ortCompilerOptions, (OptionsRelation)projectCompilerOptionsRelation);
target->SetOptionRelation(ortLinkerOptions, (OptionsRelation)projectLinkerOptionsRelation);
target->SetOptionRelation(ortIncludeDirs, (OptionsRelation)projectIncludeDirsRelation);
target->SetOptionRelation(ortLibDirs, (OptionsRelation)projectLibDirsRelation);
target->SetOptionRelation(ortResDirs, (OptionsRelation)projectResIncludeDirsRelation);
DoMakeCommands(parentNode->FirstChildElement("MakeCommands"), target);
}
}
void ProjectLoader::DoCompilerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
TiXmlElement* node = parentNode->FirstChildElement("Compiler");
if (!node)
return; // no options
TiXmlElement* child = node->FirstChildElement("Add");
while (child)
{
wxString option = cbC2U(child->Attribute("option"));
wxString dir = UnixFilename(cbC2U(child->Attribute("directory")));
if (!option.IsEmpty())
{
if (target)
target->AddCompilerOption(option);
else
m_pProject->AddCompilerOption(option);
}
if (!dir.IsEmpty())
{
if (target)
target->AddIncludeDir(dir);
else
m_pProject->AddIncludeDir(dir);
}
child = child->NextSiblingElement("Add");
}
}
void ProjectLoader::DoResourceCompilerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
TiXmlElement* node = parentNode->FirstChildElement("ResourceCompiler");
if (!node)
return; // no options
TiXmlElement* child = node->FirstChildElement("Add");
while (child)
{
wxString option = cbC2U(child->Attribute("option"));
wxString dir = UnixFilename(cbC2U(child->Attribute("directory")));
if (!option.IsEmpty())
{
if (target)
target->AddResourceCompilerOption(option);
else
m_pProject->AddResourceCompilerOption(option);
}
if (!dir.IsEmpty())
{
if (target)
target->AddResourceIncludeDir(dir);
else
m_pProject->AddResourceIncludeDir(dir);
}
child = child->NextSiblingElement("Add");
}
}
void ProjectLoader::DoLinkerOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
TiXmlElement* node = parentNode->FirstChildElement("Linker");
if (!node)
return; // no options
TiXmlElement* child = node->FirstChildElement("Add");
while (child)
{
wxString option = cbC2U(child->Attribute("option"));
wxString dir = UnixFilename(cbC2U(child->Attribute("directory")));
wxString lib = UnixFilename(cbC2U(child->Attribute("library")));
if (!option.IsEmpty())
{
if (target)
target->AddLinkerOption(option);
else
m_pProject->AddLinkerOption(option);
}
if (!lib.IsEmpty())
{
if (target)
target->AddLinkLib(lib);
else
m_pProject->AddLinkLib(lib);
}
if (!dir.IsEmpty())
{
if (target)
target->AddLibDir(dir);
else
m_pProject->AddLibDir(dir);
}
child = child->NextSiblingElement("Add");
}
child = node->FirstChildElement("LinkerExe");
if (child)
{
const wxString value = cbC2U(child->Attribute("value"));
wxString str[int(LinkerExecutableOption::Last) - 1] = {
wxT("CCompiler"),
wxT("CppCompiler"),
wxT("Linker")
};
int index;
for (index = 0; index < cbCountOf(str); ++index)
{
if (value == str[index])
break;
}
LinkerExecutableOption linkerExe = LinkerExecutableOption::AutoDetect;
if (index < cbCountOf(str))
linkerExe = LinkerExecutableOption(index + 1);
if (target)
target->SetLinkerExecutable(linkerExe);
}
}
void ProjectLoader::DoIncludesOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
TiXmlElement* node = parentNode->FirstChildElement("IncludeDirs");
if (!node)
return; // no options
TiXmlElement* child = node->FirstChildElement("Add");
while (child)
{
wxString option = UnixFilename(cbC2U(child->Attribute("option")));
if (!option.IsEmpty())
{
if (target)
target->AddIncludeDir(option);
else
m_pProject->AddIncludeDir(option);
}
child = child->NextSiblingElement("Add");
}
}
void ProjectLoader::DoLibsOptions(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
TiXmlElement* node = parentNode->FirstChildElement("LibDirs");
if (!node)
return; // no options
TiXmlElement* child = node->FirstChildElement("Add");
while (child)
{
wxString option = UnixFilename(cbC2U(child->Attribute("option")));
if (!option.IsEmpty())
{
if (target)
target->AddLibDir(option);
else
m_pProject->AddLibDir(option);
}
child = child->NextSiblingElement("Add");
}
}
void ProjectLoader::DoExtraCommands(TiXmlElement* parentNode, ProjectBuildTarget* target)
{
TiXmlElement* node = parentNode->FirstChildElement("ExtraCommands");
while (node)
{
CompileOptionsBase* base = target ? target : (CompileOptionsBase*)m_pProject;
TiXmlElement* child = node->FirstChildElement("Mode");
while (child)
{
wxString mode = cbC2U(child->Attribute("after"));
if (mode == "always")
base->SetAlwaysRunPostBuildSteps(true);
child = child->NextSiblingElement("Mode");
}
child = node->FirstChildElement("Add");
while (child)
{
wxString before;
wxString after;
if (child->Attribute("before"))
before = cbC2U(child->Attribute("before"));
if (child->Attribute("after"))
after = cbC2U(child->Attribute("after"));
if (!before.IsEmpty())
base->AddCommandsBeforeBuild(before);
if (!after.IsEmpty())
base->AddCommandsAfterBuild(after);
child = child->NextSiblingElement("Add");
}
node = node->NextSiblingElement("ExtraCommands");
}
}
void ProjectLoader::DoEnvironment(TiXmlElement* parentNode, CompileOptionsBase* base)
{
if (!base)
return;
TiXmlElement* node = parentNode->FirstChildElement("Environment");
while (node)
{
TiXmlElement* child = node->FirstChildElement("Variable");
while (child)
{
wxString name = cbC2U(child->Attribute("name"));
wxString value = cbC2U(child->Attribute("value"));
if (!name.IsEmpty())
base->SetVar(name, UnixFilename(value));
child = child->NextSiblingElement("Variable");
}
node = node->NextSiblingElement("Environment");
}
}
namespace
{
wxString makePathAbsoluteIfNeeded(const wxString& path, const wxString& basePath)
{
wxString absolute = path;
wxFileName fname = path;
if (!fname.IsAbsolute())
{
fname.MakeAbsolute(basePath);
absolute = fname.GetFullPath();
}
return absolute;
}
wxString makePathRelativeIfNeeded(const wxString& path, const wxString& basePath)
{
wxString relative = path;
wxFileName fname = path;
if (fname.IsAbsolute())
{
fname.MakeRelativeTo(basePath);
relative = fname.GetFullPath();
}
return relative;
}
wxArrayString makePathsRelativeIfNeeded(const wxArrayString& paths, const wxString& basePath)
{
wxArrayString relatives = paths;
for(std::size_t index = 0U; index < paths.Count(); ++index)
{
wxString& path = relatives[index];
path = makePathRelativeIfNeeded(path, basePath);
}
return relatives;
}
std::vector<wxString> filterOnWildcards(const wxArrayString& files, const wxString& wildCard)
{
wxString wild = wildCard;
if(wild.IsEmpty())
{
FilesGroupsAndMasks fgm;
for (unsigned i = 0; i < fgm.GetGroupsCount(); ++i)
{
wild += fgm.GetFileMasks(i);
}
}
const wxArrayString wilds = GetArrayFromString(wild, ";");
std::vector<wxString> finalFiles;
for(std::size_t file = 0; file < files.Count(); ++file)
{
const wxString& fileName = files[file];
bool MatchesWildCard = false;
for (std::size_t x = 0; x < wilds.GetCount(); ++x)
{
if (fileName.Matches(wilds[x].Lower()))
{
MatchesWildCard = true;
break;
}
}
if(MatchesWildCard)
{
finalFiles.push_back(fileName);
}
}
return finalFiles;
}
std::vector<wxString> filesInDir(const wxString& directory, const wxString& wildCard, bool recursive, const wxString& basePath)
{
const wxString directoryPath = makePathAbsoluteIfNeeded(directory, basePath);
std::vector<wxString> files;
int flags = wxDIR_FILES;
if(recursive)
{
flags = flags | wxDIR_DIRS;
}
wxArrayString filesUnfiltered;
wxDir::GetAllFiles(directoryPath, &filesUnfiltered, wxEmptyString, flags);
filesUnfiltered = makePathsRelativeIfNeeded(filesUnfiltered, basePath);
return filterOnWildcards(filesUnfiltered, wildCard);
}
} // namespace
void ProjectLoader::DoUnits(const TiXmlElement* parentNode)
{
Manager::Get()->GetLogManager()->DebugLog("Loading project files...");
m_pProject->BeginAddFiles();
int count = 0;
// TODO : we need to store all the globs, so that at save time we can filter files out, globs derived ones should not be stored as <Unit ... >
std::vector<cbProject::Glob> unitsGlobs;
const std::string UnitsGlobLabel("UnitsGlob");
const TiXmlElement* unitsGlob = parentNode->FirstChildElement(UnitsGlobLabel.c_str());
while (unitsGlob)
{
const wxString directory = cbC2U(unitsGlob->Attribute("directory"));
const wxString wildCard = cbC2U(unitsGlob->Attribute("wildcard"));
int recursive = 1;
unitsGlob->QueryIntAttribute("recursive", &recursive);
if (!directory.IsEmpty())
{
const bool isRecursive = (recursive)?true:false;
unitsGlobs.push_back(cbProject::Glob(directory, wildCard, isRecursive));
std::vector<wxString> files = filesInDir(directory, wildCard, isRecursive, m_pProject->GetBasePath());
for (std::size_t index = 0; index < files.size(); ++index)
{
const wxString filename = files[index];
ProjectFile* file = m_pProject->AddFile(-1, UnixFilename(filename));
if (!file)
Manager::Get()->GetLogManager()->DebugLog("Can't load file " + filename);
else
{
++count;
const TiXmlElement dummyUnitWithoutOptions("Unit");
DoUnitOptions(&dummyUnitWithoutOptions, file);
}
}
}
unitsGlob = unitsGlob->NextSiblingElement(UnitsGlobLabel.c_str());
}
m_pProject->SetGlobs(unitsGlobs);
const TiXmlElement* unit = parentNode->FirstChildElement("Unit");
while (unit)
{
const wxString filename = cbC2U(unit->Attribute("filename"));
if (!filename.IsEmpty())
{
ProjectFile* file = m_pProject->AddFile(-1, UnixFilename(filename));
if (!file)
Manager::Get()->GetLogManager()->DebugLog("Can't load file " + filename);
else
{
++count;
DoUnitOptions(unit, file);
}
}
unit = unit->NextSiblingElement("Unit");
}
m_pProject->EndAddFiles();
Manager::Get()->GetLogManager()->DebugLog(wxString::Format("%d files loaded", count));
}
void ProjectLoader::DoUnitOptions(const TiXmlElement* parentNode, ProjectFile* file)
{
int tempval = 0;
bool foundCompile = false;
bool foundLink = false;
bool foundCompilerVar = false;
bool foundTarget = false;
bool noTarget = false;
// Compiler* compiler = CompilerFactory::GetCompiler(m_pProject->GetCompilerID());
const TiXmlElement* node = parentNode->FirstChildElement("Option");
while (node)
{
if (node->Attribute("compilerVar"))
{
file->compilerVar = cbC2U(node->Attribute("compilerVar"));
foundCompilerVar = true;
}
//
if (node->QueryIntAttribute("compile", &tempval) == TIXML_SUCCESS)
{
file->compile = tempval != 0;
foundCompile = true;
}
//
if (node->QueryIntAttribute("link", &tempval) == TIXML_SUCCESS)
{
file->link = tempval != 0;
foundLink = true;
}
//
if (node->QueryIntAttribute("weight", &tempval) == TIXML_SUCCESS)
file->weight = tempval;
//
if (node->Attribute("virtualFolder"))
file->virtual_path = UnixFilename(cbC2U(node->Attribute("virtualFolder")));
//
if (node->Attribute("buildCommand") && node->Attribute("compiler"))
{
const wxString cmp = cbC2U(node->Attribute("compiler"));
wxString tmp = cbC2U(node->Attribute("buildCommand"));
if (!cmp.IsEmpty() && !tmp.IsEmpty())
{
tmp.Replace("\\n", "\n");
file->SetCustomBuildCommand(cmp, tmp);
if (node->QueryIntAttribute("use", &tempval) == TIXML_SUCCESS)
file->SetUseCustomBuildCommand(cmp, tempval != 0);
}
}
//
if (node->Attribute("target"))
{
wxString targetName = cbC2U(node->Attribute("target"));
if (!targetName.IsSameAs("<{~None~}>"))
{
file->AddBuildTarget(targetName);
foundTarget = true;
}
else
noTarget = true;
}
node = node->NextSiblingElement("Option");
}
// pre 1.6 versions upgrade
if (m_IsPre_1_6)
{
// make sure the "compile" and "link" flags are honored
if (!foundCompile)
file->compile = true;
if (!foundLink)
file->link = true;
if (!foundCompilerVar)
file->compilerVar = "CPP";
}
if (!foundTarget && !noTarget)
{
// add to all targets
for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
{
file->AddBuildTarget(m_pProject->GetBuildTarget(i)->GetTitle());
}
}
}
// convenience function, used in Save()
static TiXmlElement* AddElement(TiXmlElement* parent, const char* name, const char* attr = nullptr,
const wxString& attribute = wxEmptyString)
{
TiXmlElement elem(name);
if (attr)
elem.SetAttribute(attr, cbU2C(attribute));
return parent->InsertEndChild(elem)->ToElement();
}
// convenience function, used in Save()
static TiXmlElement* AddElement(TiXmlElement* parent, const char* name, const char* attr,
int attribute)
{
TiXmlElement elem(name);
if (attr)
elem.SetAttribute(attr, attribute);
return parent->InsertEndChild(elem)->ToElement();
}
// convenience function, used in Save()
static void AddArrayOfElements(TiXmlElement* parent, const char* name, const char* attr,
const wxArrayString& array, bool isPath = false)
{
if (!array.GetCount())
return;
for (unsigned int i = 0; i < array.GetCount(); ++i)
{
if (array[i].IsEmpty())
continue;
AddElement(parent, name, attr, (isPath ? UnixFilename(array[i], wxPATH_UNIX) : array[i]));
}
}
// convenience function, used in Save()
static void SaveEnvironment(TiXmlElement* parent, CompileOptionsBase* base)
{
if (!base)
return;
const StringHash& v = base->GetAllVars();
if (v.empty())
return;
// explicitly sort the keys
typedef std::map<wxString, wxString> SortedMap;
SortedMap map;
for (StringHash::const_iterator it = v.begin(); it != v.end(); ++it)
map[it->first] = it->second;
TiXmlElement* node = AddElement(parent, "Environment");
for (SortedMap::const_iterator it = map.begin(); it != map.end(); ++it)
{
TiXmlElement* elem = AddElement(node, "Variable", "name", it->first);
elem->SetAttribute("value", cbU2C(it->second));
}
}
bool ProjectLoader::Save(const wxString& filename)
{
return Save(filename, nullptr);
}
bool ProjectLoader::Save(const wxString& filename, TiXmlElement* pExtensions)
{
if (ExportTargetAsProject(filename, wxEmptyString, pExtensions))
{
m_pProject->SetModified(false);
return true;
}
return false;
}
// convenience function, used in ExportTargetAsProject()
static void SaveLinkerExecutable(TiXmlElement *linkerNode, const CompileOptionsBase &options)
{
const LinkerExecutableOption linkerExe = options.GetLinkerExecutable();
if (linkerExe > LinkerExecutableOption::AutoDetect
&& linkerExe < LinkerExecutableOption::Last)
{
wxString str[int(LinkerExecutableOption::Last) - 1] = {
wxT("CCompiler"),
wxT("CppCompiler"),
wxT("Linker")
};
AddElement(linkerNode, "LinkerExe", "value", str[int(linkerExe) - 1]);
}
}
bool ProjectLoader::ExportTargetAsProject(const wxString& filename, const wxString& onlyTarget, TiXmlElement* pExtensions)
{
const char* ROOT_TAG = "CodeBlocks_project_file";
TiXmlDocument doc;
doc.SetCondenseWhiteSpace(false);
doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
if (!rootnode)
return false;
// Compiler* compiler = CompilerFactory::GetCompiler(m_pProject->GetCompilerID());
rootnode->InsertEndChild(TiXmlElement("FileVersion"));
rootnode->FirstChildElement("FileVersion")->SetAttribute("major", PROJECT_FILE_VERSION_MAJOR);
rootnode->FirstChildElement("FileVersion")->SetAttribute("minor", PROJECT_FILE_VERSION_MINOR);
rootnode->InsertEndChild(TiXmlElement("Project"));
TiXmlElement* prjnode = rootnode->FirstChildElement("Project");
AddElement(prjnode, "Option", "title", m_pProject->GetTitle());
if (m_pProject->GetPlatforms() != spAll)
{
wxString platforms = GetStringFromPlatforms(m_pProject->GetPlatforms());
AddElement(prjnode, "Option", "platforms", platforms);
}
if (m_pProject->GetMakefile() != "Makefile")
AddElement(prjnode, "Option", "makefile", UnixFilename(m_pProject->GetMakefile(), wxPATH_UNIX));
if (m_pProject->IsMakefileCustom())
AddElement(prjnode, "Option", "makefile_is_custom", 1);
if (m_pProject->GetMakefileExecutionDir() != m_pProject->GetBasePath())
AddElement(prjnode, "Option", "execution_dir", UnixFilename(m_pProject->GetMakefileExecutionDir(), wxPATH_UNIX));
if (m_pProject->GetModeForPCH() != pchObjectDir)
AddElement(prjnode, "Option", "pch_mode", (int)m_pProject->GetModeForPCH());
if (!m_pProject->GetDefaultExecuteTarget().IsEmpty() && m_pProject->GetDefaultExecuteTarget() != m_pProject->GetFirstValidBuildTargetName())
AddElement(prjnode, "Option", "default_target", m_pProject->GetDefaultExecuteTarget());
AddElement(prjnode, "Option", "compiler", m_pProject->GetCompilerID());
wxArrayString virtualFolders = m_pProject->GetVirtualFolders();
if (virtualFolders.GetCount() > 0)
{
wxString result; // the concatenated string
for (size_t i = 0; i < virtualFolders.GetCount(); i++)
{
if (!result.IsEmpty())
result << wxT(";"); // add the delimiter
result << UnixFilename(virtualFolders[i], wxPATH_UNIX); // append Unix format folder name
}
AddElement(prjnode, "Option", "virtualFolders", result);
}
if (m_pProject->GetExtendedObjectNamesGeneration())
AddElement(prjnode, "Option", "extended_obj_names", 1);
if (m_pProject->GetShowNotesOnLoad() || !m_pProject->GetNotes().IsEmpty())
{
TiXmlElement* notesBase = AddElement(prjnode, "Option", "show_notes", m_pProject->GetShowNotesOnLoad() ? 1 : 0);
if (!m_pProject->GetNotes().IsEmpty())
{
TiXmlElement* notes = AddElement(notesBase, "notes");
TiXmlText t(m_pProject->GetNotes().mb_str(wxConvUTF8));
t.SetCDATA(true);
notes->InsertEndChild(t);
}
}
if (!m_pProject->GetCheckForExternallyModifiedFiles())
AddElement(prjnode, "Option", "check_files", 0);
if (m_pProject->MakeCommandsModified())
{
TiXmlElement* makenode = AddElement(prjnode, "MakeCommands");
AddElement(makenode, "Build", "command", m_pProject->GetMakeCommandFor(mcBuild));
AddElement(makenode, "CompileFile", "command", m_pProject->GetMakeCommandFor(mcCompileFile));
AddElement(makenode, "Clean", "command", m_pProject->GetMakeCommandFor(mcClean));
AddElement(makenode, "DistClean", "command", m_pProject->GetMakeCommandFor(mcDistClean));
AddElement(makenode, "AskRebuildNeeded", "command", m_pProject->GetMakeCommandFor(mcAskRebuildNeeded));
AddElement(makenode, "SilentBuild", "command", m_pProject->GetMakeCommandFor(mcSilentBuild));
}
prjnode->InsertEndChild(TiXmlElement("Build"));
TiXmlElement* buildnode = prjnode->FirstChildElement("Build");
for (size_t x = 0; x < m_pProject->GetBuildScripts().GetCount(); ++x)
AddElement(buildnode, "Script", "file", UnixFilename(m_pProject->GetBuildScripts().Item(x), wxPATH_UNIX));
// now decide which target we're exporting.
// remember that if onlyTarget is empty, we export all targets (i.e. normal save).
ProjectBuildTarget* onlytgt = m_pProject->GetBuildTarget(onlyTarget);
for (int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i)
{
ProjectBuildTarget* target = m_pProject->GetBuildTarget(i);
if (!target)
break;
// skip every target except the desired one
if (onlytgt && onlytgt != target)
continue;
TiXmlElement* tgtnode = AddElement(buildnode, "Target", "title", target->GetTitle());
if (target->GetPlatforms() != spAll)
{
wxString platforms = GetStringFromPlatforms(target->GetPlatforms());
AddElement(tgtnode, "Option", "platforms", platforms);
}
if (target->GetTargetType() != ttCommandsOnly)
{
TargetFilenameGenerationPolicy prefixPolicy;
TargetFilenameGenerationPolicy extensionPolicy;
target->GetTargetFilenameGenerationPolicy(prefixPolicy, extensionPolicy);
wxString outputFileName = target->GetOutputFilename();
if (extensionPolicy == tgfpPlatformDefault)
{
wxFileName fname(outputFileName);
fname.ClearExt();
outputFileName = fname.GetFullPath();
}
if ( (prefixPolicy == tgfpPlatformDefault)
&& ( (!platform::windows && target->GetTargetType() == ttDynamicLib)
|| (target->GetTargetType() == ttStaticLib) ) )
{
wxString compilerId = target->GetCompilerID();
Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
if (compiler)
{
wxFileName fname(outputFileName);
wxString outputFileNameFile(fname.GetFullName());
wxString compilerLibPrefix(compiler->GetSwitches().libPrefix);
wxString outputFileNameWOPrefix;
if (outputFileNameFile.StartsWith(compilerLibPrefix))
{
outputFileNameWOPrefix = outputFileNameFile.Mid(compilerLibPrefix.Len());
if (!outputFileNameWOPrefix.IsEmpty())
{
fname.SetFullName(outputFileNameWOPrefix);
outputFileName = fname.GetFullPath();
}
}
}
}
TiXmlElement* outnode = AddElement(tgtnode, "Option", "output", UnixFilename(outputFileName, wxPATH_UNIX));
if (target->GetTargetType() == ttDynamicLib)
{
if (target->GetDynamicLibImportFilename() != "$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME)")
outnode->SetAttribute("imp_lib", cbU2C(UnixFilename(target->GetDynamicLibImportFilename(), wxPATH_UNIX)));
if (target->GetDynamicLibImportFilename() != "$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME)")
outnode->SetAttribute("def_file", cbU2C(UnixFilename(target->GetDynamicLibDefFilename(), wxPATH_UNIX)));
}
outnode->SetAttribute("prefix_auto", prefixPolicy == tgfpPlatformDefault ? "1" : "0");
outnode->SetAttribute("extension_auto", extensionPolicy == tgfpPlatformDefault ? "1" : "0");
if (target->GetWorkingDir() != ".")
AddElement(tgtnode, "Option", "working_dir", UnixFilename(target->GetWorkingDir(), wxPATH_UNIX));
if (target->GetObjectOutput() != ".objs")
AddElement(tgtnode, "Option", "object_output", UnixFilename(target->GetObjectOutput(), wxPATH_UNIX));
if (target->GetDepsOutput() != ".deps")
AddElement(tgtnode, "Option", "deps_output", UnixFilename(target->GetDepsOutput(), wxPATH_UNIX));
}
if (!target->GetExternalDeps().IsEmpty())
AddElement(tgtnode, "Option", "external_deps", UnixFilename(target->GetExternalDeps(), wxPATH_UNIX));
if (!target->GetAdditionalOutputFiles().IsEmpty())
AddElement(tgtnode, "Option", "additional_output", UnixFilename(target->GetAdditionalOutputFiles(), wxPATH_UNIX));
AddElement(tgtnode, "Option", "type", target->GetTargetType());
AddElement(tgtnode, "Option", "compiler", target->GetCompilerID());
if (target->GetTargetType() == ttConsoleOnly && !target->GetUseConsoleRunner())
AddElement(tgtnode, "Option", "use_console_runner", 0);
if (!target->GetExecutionParameters().IsEmpty())
AddElement(tgtnode, "Option", "parameters", target->GetExecutionParameters());
if (!target->GetHostApplication().IsEmpty())
{
AddElement(tgtnode, "Option", "host_application", UnixFilename(target->GetHostApplication(), wxPATH_UNIX));
if (target->GetRunHostApplicationInTerminal())
AddElement(tgtnode, "Option", "run_host_application_in_terminal", 1);
else
AddElement(tgtnode, "Option", "run_host_application_in_terminal", 0);
}
// used in versions prior to 1.5
// if (target->GetIncludeInTargetAll())
// AddElement(tgtnode, "Option", "includeInTargetAll", 1);
if ((target->GetTargetType() == ttStaticLib || target->GetTargetType() == ttDynamicLib) && target->GetCreateDefFile())
AddElement(tgtnode, "Option", "createDefFile", 1);
if (target->GetTargetType() == ttDynamicLib && target->GetCreateStaticLib())
AddElement(tgtnode, "Option", "createStaticLib", 1);
if (target->GetOptionRelation(ortCompilerOptions) != 3) // 3 is the default
AddElement(tgtnode, "Option", "projectCompilerOptionsRelation", target->GetOptionRelation(ortCompilerOptions));
if (target->GetOptionRelation(ortLinkerOptions) != 3) // 3 is the default
AddElement(tgtnode, "Option", "projectLinkerOptionsRelation", target->GetOptionRelation(ortLinkerOptions));
if (target->GetOptionRelation(ortIncludeDirs) != 3) // 3 is the default
AddElement(tgtnode, "Option", "projectIncludeDirsRelation", target->GetOptionRelation(ortIncludeDirs));
if (target->GetOptionRelation(ortResDirs) != 3) // 3 is the default
AddElement(tgtnode, "Option", "projectResourceIncludeDirsRelation", target->GetOptionRelation(ortResDirs));
if (target->GetOptionRelation(ortLibDirs) != 3) // 3 is the default
AddElement(tgtnode, "Option", "projectLibDirsRelation", target->GetOptionRelation(ortLibDirs));
for (size_t x = 0; x < target->GetBuildScripts().GetCount(); ++x)
AddElement(tgtnode, "Script", "file", target->GetBuildScripts().Item(x));
TiXmlElement* node = AddElement(tgtnode, "Compiler");
AddArrayOfElements(node, "Add", "option", target->GetCompilerOptions());
AddArrayOfElements(node, "Add", "directory", target->GetIncludeDirs(), true);
if (node->NoChildren())
tgtnode->RemoveChild(node);
node = AddElement(tgtnode, "ResourceCompiler");
AddArrayOfElements(node, "Add", "option", target->GetResourceCompilerOptions());
AddArrayOfElements(node, "Add", "directory", target->GetResourceIncludeDirs(), true);
if (node->NoChildren())
tgtnode->RemoveChild(node);
node = AddElement(tgtnode, "Linker");
AddArrayOfElements(node, "Add", "option", target->GetLinkerOptions());
AddArrayOfElements(node, "Add", "library", target->GetLinkLibs(), true);
AddArrayOfElements(node, "Add", "directory", target->GetLibDirs(), true);
SaveLinkerExecutable(node, *target);
if (node->NoChildren())
tgtnode->RemoveChild(node);
node = AddElement(tgtnode, "ExtraCommands");
AddArrayOfElements(node, "Add", "before", target->GetCommandsBeforeBuild());
AddArrayOfElements(node, "Add", "after", target->GetCommandsAfterBuild());
if (node->NoChildren())
tgtnode->RemoveChild(node);
else
{
if (target->GetAlwaysRunPostBuildSteps())
AddElement(node, "Mode", "after", wxString("always"));
}
SaveEnvironment(tgtnode, target);
if (target->MakeCommandsModified())
{
TiXmlElement* makenode = AddElement(tgtnode, "MakeCommands");
AddElement(makenode, "Build", "command", target->GetMakeCommandFor(mcBuild));
AddElement(makenode, "CompileFile", "command", target->GetMakeCommandFor(mcCompileFile));
AddElement(makenode, "Clean", "command", target->GetMakeCommandFor(mcClean));
AddElement(makenode, "DistClean", "command", target->GetMakeCommandFor(mcDistClean));
AddElement(makenode, "AskRebuildNeeded", "command", target->GetMakeCommandFor(mcAskRebuildNeeded));
AddElement(makenode, "SilentBuild", "command", target->GetMakeCommandFor(mcSilentBuild));
}
}
// virtuals only for whole project
if (onlyTarget.IsEmpty())
{
TiXmlElement* virtnode = AddElement(prjnode, "VirtualTargets");
wxArrayString virtuals = m_pProject->GetVirtualBuildTargets();
for (size_t i = 0; i < virtuals.GetCount(); ++i)
{
const wxArrayString& group = m_pProject->GetVirtualBuildTargetGroup(virtuals[i]);
wxString groupStr = GetStringFromArray(group, ";");
if (!groupStr.IsEmpty())
{
TiXmlElement* elem = AddElement(virtnode, "Add", "alias", virtuals[i]);
elem->SetAttribute("targets", cbU2C(groupStr));
}
}
if (virtnode->NoChildren())
prjnode->RemoveChild(virtnode);
}
SaveEnvironment(buildnode, m_pProject);
TiXmlElement* node = AddElement(prjnode, "Compiler");
AddArrayOfElements(node, "Add", "option", m_pProject->GetCompilerOptions());
AddArrayOfElements(node, "Add", "directory", m_pProject->GetIncludeDirs(), true);
if (node->NoChildren())
prjnode->RemoveChild(node);
node = AddElement(prjnode, "ResourceCompiler");
AddArrayOfElements(node, "Add", "option", m_pProject->GetResourceCompilerOptions());
AddArrayOfElements(node, "Add", "directory", m_pProject->GetResourceIncludeDirs(), true);
if (node->NoChildren())
prjnode->RemoveChild(node);
node = AddElement(prjnode, "Linker");
AddArrayOfElements(node, "Add", "option", m_pProject->GetLinkerOptions());
AddArrayOfElements(node, "Add", "library", m_pProject->GetLinkLibs(), true);
AddArrayOfElements(node, "Add", "directory", m_pProject->GetLibDirs(), true);
if (node->NoChildren())
prjnode->RemoveChild(node);
node = AddElement(prjnode, "ExtraCommands");
AddArrayOfElements(node, "Add", "before", m_pProject->GetCommandsBeforeBuild());
AddArrayOfElements(node, "Add", "after", m_pProject->GetCommandsAfterBuild());
if (node->NoChildren())
prjnode->RemoveChild(node);
else
{
if (m_pProject->GetAlwaysRunPostBuildSteps())
AddElement(node, "Mode", "after", wxString("always"));
}
std::vector<wxString> filesThrougGlobs;
const std::vector<cbProject::Glob>& unitGlobs = m_pProject->GetGlobs();
for (std::size_t index = 0; index < unitGlobs.size(); ++index)
{
const cbProject::Glob& glob = unitGlobs[index];
if (TiXmlElement* unitsGlobNode = AddElement(prjnode, "UnitsGlob", "directory", glob.m_Path))
{
unitsGlobNode->SetAttribute("recursive", glob.m_Recursive ? "1" : "0");
unitsGlobNode->SetAttribute("wildcard", cbU2C(glob.m_WildCard));
}
std::vector<wxString> files = filesInDir(glob.m_Path, glob.m_WildCard, glob.m_Recursive, m_pProject->GetBasePath());
std::copy(files.begin(), files.end(), std::back_inserter(filesThrougGlobs));
}
ProjectFileArray pfa(ProjectFile::CompareProjectFiles);
for (FilesList::iterator it = m_pProject->GetFilesList().begin(); it != m_pProject->GetFilesList().end(); ++it)
{
ProjectFile* f = *it;
// do not save auto-generated files
if (f->AutoGeneratedBy())
continue;
if (std::find(filesThrougGlobs.begin(), filesThrougGlobs.end(), f->relativeFilename) != filesThrougGlobs.end())
continue;
// do not save project files that do not belong in the target we 're exporting
if (onlytgt && (onlytgt->GetFilesList().find(f) == onlytgt->GetFilesList().end()))
continue;
pfa.Add(f);
}
for (size_t i=0; i<pfa.GetCount(); ++i)
{
ProjectFile* f = pfa[i];
FileType ft = FileTypeOf(f->relativeFilename);
TiXmlElement* unitnode = AddElement(prjnode, "Unit", "filename", UnixFilename(f->relativeFilename, wxPATH_UNIX));
if (!f->compilerVar.IsEmpty())
{
const wxString ext = f->relativeFilename.AfterLast('.').Lower();
if (f->compilerVar != "CC" && (ext.IsSameAs(FileFilters::C_EXT)))
AddElement(unitnode, "Option", "compilerVar", f->compilerVar);
#ifdef __WXMSW__
else if (f->compilerVar != "WINDRES" && ext.IsSameAs(FileFilters::RESOURCE_EXT))
AddElement(unitnode, "Option", "compilerVar", f->compilerVar);
#endif
else if (f->compilerVar != "CPP") // default
AddElement(unitnode, "Option", "compilerVar", f->compilerVar);
}
if (f->compile != (ft == ftSource || ft == ftResource))
AddElement(unitnode, "Option", "compile", f->compile ? 1 : 0);
if (f->link != ( ft == ftSource || ft == ftResource
|| ft == ftObject || ft == ftResourceBin
|| ft == ftStaticLib ) )
{
AddElement(unitnode, "Option", "link", f->link ? 1 : 0);
}
if (f->weight != 50)
AddElement(unitnode, "Option", "weight", f->weight);
if (!f->virtual_path.IsEmpty())
AddElement(unitnode, "Option", "virtualFolder", UnixFilename(f->virtual_path, wxPATH_UNIX));
// loop and save custom build commands
for (pfCustomBuildMap::iterator it = f->customBuild.begin(); it != f->customBuild.end(); ++it)
{
pfCustomBuild& pfcb = it->second;
if (!pfcb.buildCommand.IsEmpty())
{
wxString tmp = pfcb.buildCommand;
tmp.Replace("\n", "\\n");
TiXmlElement* elem = AddElement(unitnode, "Option", "compiler", it->first);
elem->SetAttribute("use", pfcb.useCustomBuildCommand ? "1" : "0");
elem->SetAttribute("buildCommand", cbU2C(tmp));
}
}
if ((int)f->buildTargets.GetCount() != m_pProject->GetBuildTargetsCount())
{
for (unsigned int x = 0; x < f->buildTargets.GetCount(); ++x)
AddElement(unitnode, "Option", "target", f->buildTargets[x]);
}
/* Add a target with a weird name if no targets are present. *
* This will help us detecting a file with no targets. */
if ((int)f->buildTargets.GetCount() == 0)
AddElement(unitnode, "Option", "target", wxString("<{~None~}>"));
}
// as a last step, run all hooked callbacks
TiXmlElement* extnode = pExtensions
? prjnode->InsertEndChild(*pExtensions)->ToElement()
: AddElement(prjnode, "Extensions");
if (ProjectLoaderHooks::HasRegisteredHooks() && extnode)
ProjectLoaderHooks::CallHooks(m_pProject, extnode, false);
// Sort by tag name and if tag names are equal use the original index of the element in the
// extensions node. Do this because we assume the code which has inserted them has done so in
// some sorted manner.
// Ideally there would be no duplicates.
struct Key
{
std::string tagName;
int index;
bool operator<(const Key &o) const
{
if (tagName == o.tagName)
return index < o.index;
return tagName < o.tagName;
}
};
std::map<Key, TiXmlNode*> sortedExtensions;
int index = 0;
for (TiXmlNode *child = extnode->FirstChild(); child; child = child->NextSibling(), ++index)
{
TiXmlElement *element = child->ToElement();
if (!element)
continue;
// Skip empty elements, because we don't want to pollute the project file.
if (element->NoChildren() && element->FirstAttribute() == nullptr)
continue;
sortedExtensions.emplace(Key{element->Value(), index}, element->Clone());
}
// Clear the old node and place the elements in sorted order.
// We assume there are only element xml nodes, everything else would be lost.
extnode->Clear();
for (const std::map<Key, TiXmlNode*>::value_type &ext : sortedExtensions)
{
extnode->LinkEndChild(ext.second);
}
return cbSaveTinyXMLDocument(&doc, filename);
}
wxString ProjectLoader::GetValidCompilerID(const wxString& proposal, const wxString& scope)
{
if (CompilerFactory::GetCompiler(proposal))
return proposal;
// check the map; maybe we asked the user before
CompilerSubstitutes::iterator it = m_CompilerSubstitutes.find(proposal);
if (it != m_CompilerSubstitutes.end())
return it->second;
Compiler* compiler = nullptr;
// if compiler is a number, then this is an older version of the project file
// propose the same compiler by index
if (!proposal.IsEmpty())
{
long int idx = -1;
if (proposal.ToLong(&idx))
compiler = CompilerFactory::GetCompiler(idx);
}
if (!compiler)
{
if(!(Manager::Get()->GetConfigManager("app")->ReadBool("/environment/ignore_invalid_targets", true)))
{
wxString msg;
msg.Printf(_("The defined compiler for %s cannot be located (ID: %s).\n"
"Please choose the compiler you want to use instead and click \"OK\".\n"
"If you click \"Cancel\", the project/target will be excluded from the build."),
scope, proposal);
compiler = CompilerFactory::SelectCompilerUI(msg);
}
}
if (!compiler)
{
// allow for invalid compiler IDs to be preserved...
m_CompilerSubstitutes[proposal] = proposal;
return proposal;
}
m_OpenDirty = true;
// finally, keep the user selection in the map so we don't ask him again
m_CompilerSubstitutes[proposal] = compiler->GetID();
return compiler->GetID();
}