#include "sdk_precomp.h" #include #include #include #include #include #include #include "manager.h" #include "projectmanager.h" #include "messagemanager.h" #include "macrosmanager.h" #include "cbproject.h" #include "projectloader.h" #include "compilerfactory.h" #include "globals.h" #include "customvars.h" ProjectLoader::ProjectLoader(cbProject* project) : m_pProject(project), m_Upgraded(false), m_OpenDirty(false) { //ctor } ProjectLoader::~ProjectLoader() { //dtor } bool ProjectLoader::Open(const wxString& filename) { MessageManager* pMsg = Manager::Get()->GetMessageManager(); if (!pMsg) return false; pMsg->DebugLog(_("Loading project file...")); TiXmlDocument doc(filename.mb_str()); if (!doc.LoadFile()) 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; } DoProjectOptions(proj); DoBuild(proj); DoCompilerOptions(proj); DoResourceCompilerOptions(proj); DoLinkerOptions(proj); DoIncludesOptions(proj); DoLibsOptions(proj); DoExtraCommands(proj); DoUnits(proj); TiXmlElement* version = root->FirstChildElement("FileVersion"); if (!version) { // pre 1.1 version ConvertVersion_Pre_1_1(); // format changed also: // removed and elements and added them as child elements // in and 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"); } 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().c_str(), m_pProject->GetTitle().c_str()); if (wxMessageBox(msg, _("Question"), wxICON_QUESTION | wxYES_NO) == wxYES) { // 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(); int compilerIdx = object->GetCompilerIndex(); Compiler* compiler = CompilerFactory::Compilers[compilerIdx]; 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 = _T(".") + 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::DoProjectOptions(TiXmlElement* parentNode) { TiXmlElement* node = parentNode->FirstChildElement("Option"); if (!node) return; // no options wxString title; wxString makefile; bool makefile_custom = false; int defaultTarget = 0; int activeTarget = -1; int compilerIdx = 0; // loop through all options while (node) { if (node->Attribute("title")) title = _U(node->Attribute("title")); if (node->Attribute("makefile")) makefile = _U(node->Attribute("makefile")); if (node->Attribute("makefile_is_custom")) makefile_custom = strncmp(node->Attribute("makefile_is_custom"), "1", 1) == 0; if (node->Attribute("default_target")) defaultTarget = atoi(node->Attribute("default_target")); if (node->Attribute("active_target")) activeTarget = atoi(node->Attribute("active_target")); if (node->Attribute("compiler")) compilerIdx = GetValidCompilerIndex(atoi(node->Attribute("compiler")), _("project")); node = node->NextSiblingElement("Option"); } m_pProject->SetTitle(title); m_pProject->SetMakefile(makefile); m_pProject->SetMakefileCustom(makefile_custom); m_pProject->SetDefaultExecuteTargetIndex(defaultTarget); m_pProject->SetActiveBuildTarget(activeTarget); m_pProject->SetCompilerIndex(compilerIdx); } void ProjectLoader::DoBuild(TiXmlElement* parentNode) { TiXmlElement* node = parentNode->FirstChildElement("Build"); while (node) { 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 = 0L; wxString title = _U(node->Attribute("title")); if (!title.IsEmpty()) target = m_pProject->AddBuildTarget(title); if (target) { Manager::Get()->GetMessageManager()->DebugLog(_("Loading target %s"), title.c_str()); 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 working_dir; wxString obj_output; wxString deps_output; wxString deps; wxString added; int type = -1; int compilerIdx = m_pProject->GetCompilerIndex(); wxString parameters; wxString hostApplication; bool includeInTargetAll = true; bool createStaticLib = false; bool createDefFile = false; int projectCompilerOptionsRelation = 3; int projectLinkerOptionsRelation = 3; int projectIncludeDirsRelation = 3; int projectLibDirsRelation = 3; while (node) { if (node->Attribute("use_console_runner")) use_console_runner = strncmp(node->Attribute("use_console_runner"), "0", 1) != 0; if (node->Attribute("output")) output = _U(node->Attribute("output")); if (node->Attribute("working_dir")) working_dir = _U(node->Attribute("working_dir")); if (node->Attribute("object_output")) obj_output = _U(node->Attribute("object_output")); if (node->Attribute("deps_output")) deps_output = _U(node->Attribute("deps_output")); if (node->Attribute("external_deps")) deps = _U(node->Attribute("external_deps")); if (node->Attribute("additional_output")) added = _U(node->Attribute("additional_output")); if (node->Attribute("type")) type = atoi(node->Attribute("type")); if (node->Attribute("compiler")) compilerIdx = GetValidCompilerIndex(atoi(node->Attribute("compiler")), _("build target")); if (node->Attribute("parameters")) parameters = _U(node->Attribute("parameters")); if (node->Attribute("host_application")) hostApplication = _U(node->Attribute("host_application")); 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")); node = node->NextSiblingElement("Option"); } if (type != -1) { 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->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->SetCompilerIndex(compilerIdx); target->SetExecutionParameters(parameters); target->SetHostApplication(hostApplication); target->SetIncludeInTargetAll(includeInTargetAll); 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); } } 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 = _U(child->Attribute("option")); wxString dir = _U(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 dir = _U(child->Attribute("directory")); 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 = _U(child->Attribute("option")); wxString dir = _U(child->Attribute("directory")); wxString lib = _U(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"); } } 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 = _U(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 = _U(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 = _U(child->Attribute("before")); if (mode == _T("always")) base->SetAlwaysRunPreBuildSteps(true); mode = _U(child->Attribute("after")); if (mode == _T("always")) base->SetAlwaysRunPostBuildSteps(true); child = child->NextSiblingElement("Mode"); } child = node->FirstChildElement("Add"); while (child) { wxString before; wxString after; if (child->Attribute("before")) before = _U(child->Attribute("before")); if (child->Attribute("after")) after = _U(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; CustomVars& vars = base->GetCustomVars(); TiXmlElement* node = parentNode->FirstChildElement("Environment"); while (node) { TiXmlElement* child = node->FirstChildElement("Variable"); while (child) { wxString name = _U(child->Attribute("name")); wxString value = _U(child->Attribute("value")); if (!name.IsEmpty()) vars.Add(name, value); child = child->NextSiblingElement("Variable"); } node = node->NextSiblingElement("Environment"); } } void ProjectLoader::DoUnits(TiXmlElement* parentNode) { Manager::Get()->GetMessageManager()->DebugLog(_U("Loading project files...")); TiXmlElement* unit = parentNode->FirstChildElement("Unit"); while (unit) { wxString filename = _U(unit->Attribute("filename")); if (!filename.IsEmpty()) { ProjectFile* file = m_pProject->AddFile(-1, filename); if (!file) Manager::Get()->GetMessageManager()->DebugLog(_("Can't load file '%s'"), filename.c_str()); else DoUnitOptions(unit, file); } unit = unit->NextSiblingElement("Unit"); } } void ProjectLoader::DoUnitOptions(TiXmlElement* parentNode, ProjectFile* file) { int tempval = 0; bool foundCompile = false; bool foundLink = false; bool foundCompilerVar = false; TiXmlElement* node = parentNode->FirstChildElement("Option"); while (node) { if (node->Attribute("compilerVar")) { file->compilerVar = _U(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->QueryIntAttribute("useBuildCommand", &tempval) == TIXML_SUCCESS) file->useCustomBuildCommand = tempval != 0; // if (node->Attribute("buildCommand")) { wxString tmp = _U(node->Attribute("buildCommand")); if (!tmp.IsEmpty()) { tmp.Replace(_T("\\n"), _T("\n")); file->buildCommand = tmp; } } // if (node->QueryIntAttribute("autoDeps", &tempval) == TIXML_SUCCESS) file->autoDeps = tempval != 0; // if (node->Attribute("customDeps")) { wxString tmp = _U(node->Attribute("customDeps")); if (!tmp.IsEmpty()) { tmp.Replace(_T("\\n"), _T("\n")); file->customDeps = tmp; } } // if (node->Attribute("objectName")) { wxFileName objName(_U(node->Attribute("objectName"))); FileType ft = FileTypeOf(file->relativeFilename); if (ft != ftResource && ft != ftResourceBin) { if (objName.GetExt() != CompilerFactory::Compilers[m_pProject->GetCompilerIndex()]->GetSwitches().objectExtension) file->SetObjName(file->relativeFilename); } } // if (node->Attribute("target")) file->AddBuildTarget(_U(node->Attribute("target"))); node = node->NextSiblingElement("Option"); } // make sure the "compile" and "link" flags are honored if (!foundCompile) file->compile = true; if (!foundLink) file->link = true; if (!foundCompilerVar) file->compilerVar = _T("CPP"); } bool ProjectLoader::Save(const wxString& filename) { wxString buffer; wxArrayString array; CustomVars* vars = 0; buffer << _T("") << _T('\n'); buffer << _T("") << _T('\n'); buffer << _T("") << _T('\n'); buffer << _T('\t') << _T("") << _T('\n'); buffer << _T('\t') << _T("") << _T('\n'); buffer << _T('\t') << _T('\t') << _T("") << _T('\n'); buffer << _T("") << _T('\n'); wxFile file(filename, wxFile::write); if (cbWrite(file,buffer)) { m_pProject->SetModified(false); return true; } return false; } void ProjectLoader::SaveCompilerOptions(wxString& buffer, CompileOptionsBase* object, int nrOfTabs) { wxString compopts; BeginOptionSection(compopts, _T("Compiler"), nrOfTabs); bool hasCompOpts = DoOptionSection(compopts, object->GetCompilerOptions(), nrOfTabs + 1, _T("option")); bool hasCompDirs = DoOptionSection(compopts, object->GetIncludeDirs(), nrOfTabs + 1, _T("directory")); if (hasCompOpts || hasCompDirs) { EndOptionSection(compopts, _T("Compiler"), nrOfTabs); buffer << compopts; } } void ProjectLoader::SaveResourceCompilerOptions(wxString& buffer, CompileOptionsBase* object, int nrOfTabs) { wxString compopts; BeginOptionSection(compopts, _T("ResourceCompiler"), nrOfTabs); bool hasCompDirs = DoOptionSection(compopts, object->GetResourceIncludeDirs(), nrOfTabs + 1, _T("directory")); if (hasCompDirs) { EndOptionSection(compopts, _T("ResourceCompiler"), nrOfTabs); buffer << compopts; } } void ProjectLoader::SaveLinkerOptions(wxString& buffer, CompileOptionsBase* object, int nrOfTabs) { wxString linkopts; BeginOptionSection(linkopts, _T("Linker"), nrOfTabs); bool hasLinkOpts = DoOptionSection(linkopts, object->GetLinkerOptions(), nrOfTabs + 1, _T("option")); bool hasLibs = DoOptionSection(linkopts, object->GetLinkLibs(), nrOfTabs + 1, _T("library")); bool hasLinkDirs = DoOptionSection(linkopts, object->GetLibDirs(), nrOfTabs + 1, _T("directory")); if (hasLinkOpts || hasLibs || hasLinkDirs) { EndOptionSection(linkopts, _T("Linker"), nrOfTabs); buffer << linkopts; } } void ProjectLoader::SaveEnvironment(wxString& buffer, CustomVars* vars, int nrOfTabs) { if (!vars) return; const VarsArray& v = vars->GetVars(); if (v.GetCount() == 0) return; for (int x = 0; x < nrOfTabs; ++x) buffer << _T('\t'); buffer << _T("") << _T('\n'); for (unsigned int i = 0; i < v.GetCount(); ++i) { Var& var = v[i]; for (int x = 0; x <= nrOfTabs; ++x) buffer << _T('\t'); buffer << _T("") << _T('\n'); } for (int x = 0; x < nrOfTabs; ++x) buffer << _T('\t'); buffer << _T("") << _T('\n'); } void ProjectLoader::BeginOptionSection(wxString& buffer, const wxString& sectionName, int nrOfTabs) { wxString local; for (int i = 0; i < nrOfTabs; ++i) local << _T('\t'); local << _T("<") << sectionName << _T(">") << _T('\n'); buffer << local; } bool ProjectLoader::DoOptionSection(wxString& buffer, const wxArrayString& array, int nrOfTabs, const wxString& optionName) { if (!array.GetCount()) return false; bool empty = true; wxString local; for (unsigned int i = 0; i < array.GetCount(); ++i) { if (array[i].IsEmpty()) continue; empty = false; for (int x = 0; x < nrOfTabs; ++x) local << _T('\t'); local << _T("") << _T('\n'); } buffer << local; return !empty; } void ProjectLoader::EndOptionSection(wxString& buffer, const wxString& sectionName, int nrOfTabs) { wxString local; for (int i = 0; i < nrOfTabs; ++i) local << _T('\t'); local << _T("") << _T('\n'); buffer << local; } void ProjectLoader::SaveOptions(wxString& buffer, const wxArrayString& array, const wxString& sectionName, int nrOfTabs, const wxString& optionName, const wxString& extra) { if (!array.GetCount()) return; wxString local; BeginOptionSection(local, sectionName, nrOfTabs); if (!extra.IsEmpty()) { for (int i = 0; i < nrOfTabs + 1; ++i) local << _T('\t'); local << extra << _T('\n'); } bool notEmpty = DoOptionSection(local, array, nrOfTabs + 1, optionName); if (notEmpty || !extra.IsEmpty()) { EndOptionSection(local, sectionName, nrOfTabs); buffer << local; } } int ProjectLoader::GetValidCompilerIndex(int proposal, const wxString& scope) { if (CompilerFactory::CompilerIndexOK(proposal)) return proposal; m_OpenDirty = true; wxArrayString compilers; for (unsigned int i = 0; i < CompilerFactory::Compilers.GetCount(); ++i) { compilers.Add(CompilerFactory::Compilers[i]->GetName()); } wxString msg; msg.Printf(_("The specified compiler does not exist.\nPlease select the compiler to use for the %s:"), scope.c_str()); proposal = wxGetSingleChoiceIndex(msg, _("Select compiler"), compilers); if (proposal == -1) { wxMessageBox(_("Setting to default compiler..."), _("Warning"), wxICON_WARNING); return CompilerFactory::GetDefaultCompilerIndex(); } return proposal; }