Merge pull request #2 from Thraix/xml

Change config file to use xml instead
This commit is contained in:
Tim Håkansson
2020-03-01 20:19:01 +01:00
committed by GitHub
25 changed files with 1738 additions and 459 deletions
+25 -16
View File
@@ -1,4 +1,4 @@
# This Makefile was generated using MakeGen v1.1.8 made by Tim Håkansson # This Makefile was generated using MakeGen v1.3.0 made by Tim Håkansson
# and is licensed under MIT. Full source of the project can be found at # and is licensed under MIT. Full source of the project can be found at
# https://github.com/Thraix/MakeGen # https://github.com/Thraix/MakeGen
CC=@g++ CC=@g++
@@ -7,8 +7,8 @@ MKDIR_P=mkdir -p
BIN=bin/ BIN=bin/
OBJPATH=$(BIN)intermediates OBJPATH=$(BIN)intermediates
INCLUDES= INCLUDES=
OBJECTS=$(OBJPATH)/ConfigCLI.o $(OBJPATH)/ConfigFile.o $(OBJPATH)/HFileGen.o $(OBJPATH)/IncludeDeps.o $(OBJPATH)/Makefile.o $(OBJPATH)/Utils.o $(OBJPATH)/main.o OBJECTS=$(OBJPATH)/ConfigCLI.o $(OBJPATH)/ConfigFile.o $(OBJPATH)/HFileGen.o $(OBJPATH)/IncludeDeps.o $(OBJPATH)/Makefile.o $(OBJPATH)/Utils.o $(OBJPATH)/ConfigFileConf.o $(OBJPATH)/main.o $(OBJPATH)/XML.o $(OBJPATH)/XMLObject.o
CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3 -D_DEBUG CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3
LIBDIR= LIBDIR=
LDFLAGS= LDFLAGS=
LIBS=$(LIBDIR) LIBS=$(LIBDIR)
@@ -33,24 +33,33 @@ $(OUTPUT): $(OBJECTS)
install: all install: all
$(info Installing MakeGen to /usr/bin/) $(info Installing MakeGen to /usr/bin/)
@cp $(OUTPUT) /usr/bin/makegen @cp $(OUTPUT) /usr/bin/makegen
$(OBJPATH)/ConfigCLI.o : src/ConfigCLI.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h $(OBJPATH)/ConfigCLI.o : src/ConfigCLI.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h src/ConfigUtils.h src/FileUtils.h src/Utils.h src/xml/XMLObject.h
$(info -[14%]- $<) $(info -[10%]- $<)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/ConfigFile.o : src/ConfigFile.cpp src/ConfigFile.h src/FileUtils.h src/Common.h src/Utils.h $(OBJPATH)/ConfigFile.o : src/ConfigFile.cpp src/ConfigFile.h src/ConfigUtils.h src/Common.h src/FileUtils.h src/Utils.h src/xml/XMLObject.h src/compatibility/ConfigFileConf.h src/xml/XML.h
$(info -[28%]- $<) $(info -[20%]- $<)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/HFileGen.o : src/HFileGen.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h src/HFileGen.h $(OBJPATH)/HFileGen.o : src/HFileGen.cpp src/FileUtils.h src/Common.h src/Utils.h src/HFileGen.h src/ConfigFile.h src/ConfigUtils.h src/xml/XMLObject.h
$(info -[42%]- $<) $(info -[30%]- $<)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/IncludeDeps.o : src/IncludeDeps.cpp src/Common.h src/IncludeDeps.h src/ConfigFile.h src/FileUtils.h src/Utils.h $(OBJPATH)/IncludeDeps.o : src/IncludeDeps.cpp src/Common.h src/IncludeDeps.h src/ConfigFile.h src/ConfigUtils.h src/FileUtils.h src/Utils.h src/xml/XMLObject.h
$(info -[57%]- $<) $(info -[40%]- $<)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/Makefile.o : src/Makefile.cpp src/IncludeDeps.h src/ConfigFile.h src/FileUtils.h src/Common.h src/Utils.h src/Makefile.h $(OBJPATH)/Makefile.o : src/Makefile.cpp src/IncludeDeps.h src/ConfigFile.h src/ConfigUtils.h src/Common.h src/FileUtils.h src/Utils.h src/xml/XMLObject.h src/Makefile.h
$(info -[71%]- $<) $(info -[50%]- $<)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/Utils.o : src/Utils.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h $(OBJPATH)/Utils.o : src/Utils.cpp src/ConfigFile.h src/ConfigUtils.h src/Common.h src/FileUtils.h src/Utils.h src/xml/XMLObject.h
$(info -[85%]- $<) $(info -[60%]- $<)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/main.o : src/main.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h src/FileUtils.h src/Utils.h src/HFileGen.h src/Makefile.h src/Timer.h $(OBJPATH)/ConfigFileConf.o : src/compatibility/ConfigFileConf.cpp src/compatibility/ConfigFileConf.h
$(info -[70%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/main.o : src/main.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h src/ConfigUtils.h src/FileUtils.h src/Utils.h src/xml/XMLObject.h src/HFileGen.h src/Makefile.h src/Timer.h
$(info -[80%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/XML.o : src/xml/XML.cpp src/xml/XML.h src/xml/XMLObject.h src/xml/XMLException.h
$(info -[90%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/XMLObject.o : src/xml/XMLObject.cpp src/xml/XMLException.h src/xml/XMLObject.h
$(info -[100%]- $<) $(info -[100%]- $<)
$(CC) $(CFLAGS) -o $@ $< $(CC) $(CFLAGS) -o $@ $<
-19
View File
@@ -1,19 +0,0 @@
#libs
#libdirs
#includedirs
#defines
_DEBUG
#compileflags
#dependencies
#srcdir
src/
#outputdir
bin/
#projectname
MakeGen
#outputname
makegen
#executable
true
#generatehfile
false
+22
View File
@@ -0,0 +1,22 @@
<makegen>
<configuration name="Release">
<generatehfile>false</generatehfile>
<hfilename>MakeGen.h</hfilename>
<outputdir>bin/</outputdir>
<outputname>makegen</outputname>
<outputtype>executable</outputtype>
<projectname>MakeGen</projectname>
<srcdir>src/</srcdir>
</configuration>
<configuration name="Debug">
<define>_DEBUG</define>
<generatehfile>false</generatehfile>
<hfilename>MakeGen.h</hfilename>
<outputdir>bin/</outputdir>
<outputname>makegen</outputname>
<outputtype>executable</outputtype>
<projectname>MakeGen</projectname>
<srcdir>src/</srcdir>
</configuration>
<version>v1.3.0</version>
</makegen>
+21 -6
View File
@@ -9,10 +9,10 @@
// Major changes, might not be backwards compatible // Major changes, might not be backwards compatible
#define MAKEGEN_VERSION_MAJOR 1 #define MAKEGEN_VERSION_MAJOR 1
// Release, should be backwards compatible // Release, should be backwards compatible with any minor version
#define MAKEGEN_VERSION_RELEASE 2 #define MAKEGEN_VERSION_RELEASE 3
// Minor changes, generally bug fixes // Minor changes, generally bug fixes
#define MAKEGEN_VERSION_MINOR 1 #define MAKEGEN_VERSION_MINOR 0
#define MAKEGEN_VERSION ("v" STR(MAKEGEN_VERSION_MAJOR) "." STR(MAKEGEN_VERSION_RELEASE) "." STR(MAKEGEN_VERSION_MINOR)) #define MAKEGEN_VERSION ("v" STR(MAKEGEN_VERSION_MAJOR) "." STR(MAKEGEN_VERSION_RELEASE) "." STR(MAKEGEN_VERSION_MINOR))
@@ -29,9 +29,9 @@ const static unsigned int FLAG_SIMPLE = BIT(9);
const static unsigned int FLAG_CONFIG = BIT(10); const static unsigned int FLAG_CONFIG = BIT(10);
#define LOG_INFO(...) Log(__VA_ARGS__); std::cout << std::endl #define LOG_INFO(...) LogHelper(__VA_ARGS__)
#define LOG_WARNING(...) Log(__VA_ARGS__); std::cout << std::endl #define LOG_WARNING(...) LogHelper(__VA_ARGS__)
#define LOG_ERROR(...) Log(__VA_ARGS__); std::cout << std::endl #define LOG_ERROR(...) LogHelper(__VA_ARGS__)
template <typename T> template <typename T>
void Log(const T& var) void Log(const T& var)
@@ -45,3 +45,18 @@ void Log(const T& var, const Ts& ...vars)
Log(var); Log(var);
Log(vars...); Log(vars...);
} }
template <typename T, typename ...Ts>
void LogHelper(const T& var)
{
Log(var);
std::cout << std::endl;
}
template <typename T, typename ...Ts>
void LogHelper(const T& var, const Ts& ...vars)
{
Log(var);
Log(vars...);
std::cout << std::endl;
}
+69 -109
View File
@@ -8,7 +8,7 @@
void ConfigCLI::DisplayCLIHelp() void ConfigCLI::DisplayCLIHelp()
{ {
LOG_INFO(1+(char*)R"( LOG_INFO(1+(char*)R"(
MakeGen conf is used to create, modify and query the makegen.conf file. MakeGen conf is used to create, modify and query the makegen.xml file.
Usage: makegen conf <command> [<args>] [--help] Usage: makegen conf <command> [<args>] [--help]
@@ -49,7 +49,7 @@ Usage: makegen conf add <setting> <value> [<values>]
Valid settings are: Valid settings are:
library Library library Library
libdir Library directory librarydir Library directory
includedir Include directory includedir Include directory
define Preprocessor define define Preprocessor define
cflag g++ compiler flags cflag g++ compiler flags
@@ -65,7 +65,7 @@ Usage: makegen conf remove <setting> <value> [<
Valid settings are Valid settings are
library Library name library Library name
libdir Library directory librarydir Library directory
includedir Include directory includedir Include directory
define Preprocessor define define Preprocessor define
cflag g++ compiler flags cflag g++ compiler flags
@@ -81,13 +81,13 @@ Usage: makegen conf set <setting> <value>
Valid string settings are: Valid string settings are:
outputdir Directory of the compiled output outputdir Directory of the compiled output
output Name of the output executable/library outputname Name of the output executable/library
name Name of the project projectname Name of the project
outputtype Type of the output, valid values are executable, sharedlibrary
and staticlibrary
hfile Name of the generated project h-file hfile Name of the generated project h-file
Valid boolean settings are: Valid boolean settings are:
executable Specifies if the project be compiled as executable or library
shared Specifies if the library should be compiled as shared.
genhfile Specifies if MakeGen should generate a project h-file genhfile Specifies if MakeGen should generate a project h-file
Boolean values can be set to either true/t/yes/y or false/f/no/n)"); Boolean values can be set to either true/t/yes/y or false/f/no/n)");
@@ -102,50 +102,42 @@ Usage: makegen conf get <setting>
Valid settings are: Valid settings are:
library Library name library Library name
libdir Library directory librarydir Library directory
includedir Include directory includedir Include directory
define Preprocessor define define Preprocessor define
cflag g++ compiler flags cflag g++ compiler flags
dependency Project which current project depends on dependency Project which current project depends on
outputdir Directory of the compiled output outputdir Directory of the compiled output
output Name of the output executable/library outputname Name of the output executable/library
name Name of the project outputtype Type of the output, valid values are executable, sharedlibrary
and staticlibrary
projectname Name of the project
hfile Name of the generated project h-file hfile Name of the generated project h-file
executable Specifies if the project be compiled as executable or library
shared Specifies if the library should be compiled as shared.
genhfile Specifies if MakeGen should generate a project h-file)"); genhfile Specifies if MakeGen should generate a project h-file)");
} }
std::map<std::string, std::vector<std::string>*> ConfigCLI::GetSettingVectorMap(ConfigFile& config)
{
return {
{"library",&config.libs},
{"libdir",&config.libdirs},
{"includedir",&config.includedirs},
{"define",&config.defines},
{"cflag",&config.flags},
{"dependency",&config.dependencies}
};
}
ConfigSetting ConfigCLI::CLIStringToSetting(const std::string& s)
std::map<std::string, std::string*> ConfigCLI::GetSettingStringMap(ConfigFile& config)
{ {
return { static std::map<std::string, ConfigSetting> map{
{"outputdir", &config.outputdir}, {"srcdir", ConfigSetting::SourceDir},
{"output", &config.outputname}, {"outputdir", ConfigSetting::OutputDir},
{"name", &config.projectname}, {"outputname", ConfigSetting::OutputName},
{"hfile", &config.hFile}, {"outputtype", ConfigSetting::OutputType},
}; {"projectname", ConfigSetting::ProjectName},
} {"hfile", ConfigSetting::HFileName},
{"library", ConfigSetting::Library},
std::map<std::string, bool*> ConfigCLI::GetSettingBoolMap(ConfigFile& config) {"librarydir", ConfigSetting::LibraryDir},
{ {"includedir", ConfigSetting::IncludeDir},
return { {"define", ConfigSetting::Define},
{"executable", &config.executable}, {"cflag", ConfigSetting::CFlag},
{"shared", &config.shared}, {"dependency", ConfigSetting::Dependency},
{"genhfile", &config.generateHFile} {"genhfile", ConfigSetting::GenerateHFile},
}; };
auto it = map.find(s);
if(it == map.end())
return ConfigSetting::Invalid;
return it->second;
} }
int ConfigCLI::Gen(int argc, char** argv) int ConfigCLI::Gen(int argc, char** argv)
@@ -168,7 +160,7 @@ int ConfigCLI::Gen(int argc, char** argv)
} }
if(option == "default") if(option == "default")
{ {
ConfigFile{}.Save(); ConfigFile{FileUtils::GetRealPath("."),0}.Save();
return 0; return 0;
} }
else else
@@ -191,25 +183,24 @@ int ConfigCLI::Add(int argc, char** argv, ConfigFile& config)
return 1; return 1;
} }
auto settingMap = GetSettingVectorMap(config); ConfigSetting setting = CLIStringToSetting(argv[1]);
auto it = settingMap.find(argv[1]); if(!ConfigUtils::IsVectorSetting(setting))
if(it == settingMap.end())
{ {
LOG_ERROR("Invalid setting: ", argv[1]); if(setting == ConfigSetting::Invalid)
{
LOG_ERROR("No such setting: ", argv[1]);
}
else
{
LOG_ERROR("Cannot remove setting which only supports one argument");
LOG_ERROR("use set instead.");
}
return 1; return 1;
} }
std::vector<std::string>* setting = it->second;
std::set<std::string> settingSet{setting->begin(), setting->end()};
for(int i = 2; i<argc;++i) for(int i = 2; i<argc;++i)
{ {
auto res = settingSet.emplace(argv[i]); config.AddSettingVectorString(setting, argv[i]);
if(!res.second)
{
LOG_ERROR("Duplicate value: ", argv[i]);
} }
}
*setting = {settingSet.begin(), settingSet.end()};
config.Save(); config.Save();
return 0; return 0;
@@ -228,27 +219,25 @@ int ConfigCLI::Remove(int argc, char** argv, ConfigFile& config)
return 1; return 1;
} }
auto settingMap = GetSettingVectorMap(config); ConfigSetting setting = CLIStringToSetting(argv[1]);
auto it = settingMap.find(argv[1]); if(!ConfigUtils::IsVectorSetting(setting))
if(it == settingMap.end())
{ {
LOG_ERROR("Invalid setting: ", argv[1]); if(setting == ConfigSetting::Invalid)
{
LOG_ERROR("No such setting: ", argv[1]);
}
else
{
LOG_ERROR("Cannot remove setting which only supports one argument");
LOG_ERROR("use set instead.");
}
return 1; return 1;
} }
std::vector<std::string>* setting = it->second;
std::set<std::string> settingSet{setting->begin(), setting->end()};
for(int i = 2; i<argc;++i) for(int i = 2; i<argc;++i)
{ {
auto it = settingSet.find(argv[i]); config.RemoveSettingVectorString(setting, argv[i]);
if(it == settingSet.end())
{
LOG_ERROR("No such value in setting: ", argv[i]);
} }
else
settingSet.erase(it);
}
*setting = {settingSet.begin(), settingSet.end()};
config.Save(); config.Save();
return 0; return 0;
@@ -267,31 +256,22 @@ int ConfigCLI::Set(int argc, char** argv, ConfigFile& config)
return 1; return 1;
} }
auto settingStringMap = GetSettingStringMap(config); ConfigSetting setting = CLIStringToSetting(argv[1]);
auto it1 = settingStringMap.find(argv[1]); if(!ConfigUtils::IsStringSetting(setting) && !ConfigUtils::IsBoolSetting(setting))
if(it1 == settingStringMap.end())
{ {
auto settingBoolMap = GetSettingBoolMap(config); if(setting == ConfigSetting::Invalid)
auto it2 = settingBoolMap.find(argv[1]);
if(it2 == settingBoolMap.end())
{ {
LOG_ERROR("Invalid setting: ", argv[1]); LOG_ERROR("No such setting: ", argv[1]);
return 1;
} }
std::string b = argv[2];
if(b == "true" || b == "t" || b == "yes" || b == "y")
*(it2->second) = true;
else if(b == "false" || b == "f" || b == "no" || b == "n")
*it2->second = false;
else else
{ {
LOG_ERROR("Invalid boolean value: ", argv[2]); LOG_ERROR("Cannot set setting which supports multiple arguments");
LOG_ERROR("use add or remove instead.");
}
return 1; return 1;
} }
config.Save();
return 0; config.SetSettingString(setting, argv[2]);
}
*it1->second = argv[2];
config.Save(); config.Save();
return 0; return 0;
} }
@@ -308,29 +288,9 @@ int ConfigCLI::Get(int argc, char** argv, ConfigFile& config)
LOG_ERROR("get needs exactly one parameter"); LOG_ERROR("get needs exactly one parameter");
return 1; return 1;
} }
auto settingVectorMap = GetSettingVectorMap(config); ConfigSetting setting = CLIStringToSetting(argv[1]);
auto itV = settingVectorMap.find(argv[1]); std::vector<std::string> vector = config.GetSetting(setting);
if(itV == settingVectorMap.end()) for(auto it = vector.begin(); it != vector.end(); ++it)
{
auto settingStringMap = GetSettingStringMap(config);
auto itS = settingStringMap.find(argv[1]);
if(itS == settingStringMap.end())
{
auto settingBoolMap = GetSettingBoolMap(config);
auto itB = settingBoolMap.find(argv[1]);
if(itB == settingBoolMap.end())
{
LOG_ERROR("Invalid setting: ", argv[1]);
return 1;
}
bool* t = itB->second;
LOG_INFO(*itB->second ? "true" : "false");
return 0;
}
LOG_INFO(*itS->second);
return 0;
}
for(auto it = itV->second->begin(); it != itV->second->end(); ++it)
{ {
LOG_INFO(*it); LOG_INFO(*it);
} }
@@ -351,7 +311,7 @@ int ConfigCLI::Main(int argc, char** argv)
{ {
if(config) if(config)
{ {
LOG_ERROR("Config file already exist (makegen.conf)"); LOG_ERROR("Config file already exist (", CONFIG_FILENAME, ")");
return 1; return 1;
} }
return Gen(argc-1, &argv[1]); return Gen(argc-1, &argv[1]);
+1 -3
View File
@@ -14,9 +14,7 @@ struct ConfigCLI
static void DisplaySetHelp(); static void DisplaySetHelp();
static void DisplayGetHelp(); static void DisplayGetHelp();
static std::map<std::string, std::vector<std::string>*> GetSettingVectorMap(ConfigFile& config); static ConfigSetting CLIStringToSetting(const std::string& s);
static std::map<std::string, std::string*> GetSettingStringMap(ConfigFile& config);
static std::map<std::string, bool*> GetSettingBoolMap(ConfigFile& config);
static int Gen(int argc, char** argv); static int Gen(int argc, char** argv);
static int Add(int argc, char** argv, ConfigFile& config); static int Add(int argc, char** argv, ConfigFile& config);
+387 -225
View File
@@ -1,43 +1,327 @@
#include "ConfigFile.h" #include "ConfigFile.h"
#include "FileUtils.h" #include "FileUtils.h"
#include "compatibility/ConfigFileConf.h"
#include "xml/XML.h"
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#define FLAG_NONE 0 ConfigFile::ConfigFile(const std::string& path, int)
#define FLAG_VECTOR 1 : configPath{path}
#define FLAG_STRING 2
#define FLAG_BOOL 3
ConfigFile::ConfigFile()
: outputdir("bin/"), srcdir("src/"), outputname(""), projectname(FileUtils::GetCurrentDirectory()), hFile(""), executable(true), shared(true), generateHFile(false)
{ {
// Converts project name (current directory) to lowercase // Converts project name (current directory) to lowercase
// and replace whitespace with underscore // and replace whitespace with underscore
std::transform( // Create xml
projectname.begin(), XMLObject makegen("makegen", {}, std::map<std::string, std::vector<XMLObject>>{});
projectname.end(),
std::back_inserter(outputname),
[](unsigned char c)
{
if(c == ' ')
return '_';
return (char)std::tolower(c);
});
// Removes all other characters // Version, target and configuration is probably going to be used in the future
auto it = std::remove_if( makegen.AddXMLObject(XMLObject("version", {}, "v1.3.0"));
outputname.begin(), makegen.AddXMLObject(XMLObject("target", {}, "Release"));
outputname.end(),
[](unsigned char c)
{
return (c < '0' || c > '9') && (c < 'a' || c > 'z') && c != '_';
});
outputname.erase(it, outputname.end());
// Add suffix XMLObject configuration("configuration", {{"name", "Release"}}, std::map<std::string, std::vector<XMLObject>>{});
outputname += ".out"; configuration.AddXMLObject(XMLObject("projectname", {}, ConfigUtils::GetDefaultProjectName(configPath)));
configuration.AddXMLObject(XMLObject("outputname", {}, ConfigUtils::GetDefaultOutputName(configPath)));
configuration.AddXMLObject(XMLObject("srcdir", {}, "src/"));
configuration.AddXMLObject(XMLObject("outputdir", {}, "bin/"));
configuration.AddXMLObject(XMLObject("hfilename", {}, ConfigUtils::GetDefaultHFileName(configPath)));
configuration.AddXMLObject(XMLObject("outputtype", {}, "executable"));
configuration.AddXMLObject(XMLObject("generatehfile", {}, "false"));
makegen.AddXMLObject(configuration);
config = makegen;
Init();
}
ConfigFile::ConfigFile(const std::string& path)
: config{XML::FromFile(path + CONFIG_FILENAME)}, configPath{path}
{
Init();
}
ConfigFile::ConfigFile(XMLObject& config, const std::string& path)
: config{config}, configPath{path}
{
Init();
}
void ConfigFile::Init()
{
const std::vector<XMLObject>* targetXml = config.GetObjectPtr("target");
target = "Release";
if(!targetXml || targetXml->size() == 0)
{
LOG_ERROR("No target found in config file. Using target=", target);
return;
}
if(targetXml->size() > 1)
LOG_ERROR("To many targets in config file. Using target=", (*targetXml)[0].GetText());
if(targetXml->size() > 0)
target = (*targetXml)[0].GetText();
}
std::string& ConfigFile::GetSettingString(ConfigSetting setting)
{
// Adding it to the cache since we need to return a reference
if(!ConfigUtils::IsStringSetting(setting))
{
LOG_ERROR("Invalid string setting");
return cache.strings.emplace("invalid", "").first->second;
}
std::string sSetting = ConfigUtils::GetSettingName(setting);
auto it = cache.strings.find(sSetting);
if(it != cache.strings.end())
return it->second;
const std::vector<XMLObject>* values = GetConfiguration().GetObjectPtr(sSetting);
// No value found, using default
if(values == nullptr)
return cache.strings.emplace(sSetting, ConfigUtils::GetDefaultSettingString(setting, configPath)).first->second;
if(values->size() != 1)
{
LOG_ERROR("To many arguments for setting using first: ", (int)setting, "=", (*values)[0].GetText());
}
std::string s = (*values)[0].GetText();
if(ConfigUtils::IsDirectory(setting) && s[s.size()-1] != '/')
s += '/';
return cache.strings.emplace(sSetting, s).first->second;
}
bool ConfigFile::GetSettingBool(ConfigSetting setting)
{
if(setting == ConfigSetting::Invalid)
{
LOG_ERROR("Invalid config setting");
return false;
}
std::string sSetting = ConfigUtils::GetSettingName(setting);
auto it = cache.bools.find(sSetting);
if(it != cache.bools.end())
return it->second;
const std::vector<XMLObject>* values = GetConfiguration().GetObjectPtr(sSetting);//,
if(values == nullptr)
return cache.bools.emplace(sSetting, ConfigUtils::GetDefaultSettingBool(setting)).first->second;
if(values->size() != 1)
{
LOG_ERROR("To many arguments for setting using first: ", (int)setting, "=", (*values)[0].GetText());
}
return cache.bools.emplace(sSetting, (*values)[0].GetText() == "true").first->second;
}
std::vector<std::string>& ConfigFile::GetSettingVectorString(ConfigSetting setting)
{
std::string sSetting = ConfigUtils::GetSettingName(setting);
auto it = cache.vecStrings.find(sSetting);
if(it != cache.vecStrings.end())
return it->second;
const std::vector<XMLObject>* values = GetConfiguration().GetObjectPtr(sSetting);
if(values == nullptr)
return cache.vecStrings.emplace(sSetting, std::vector<std::string>{}).first->second;
std::vector<std::string> strings;
strings.reserve(values->size());
for(auto it = values->begin(); it != values->end(); ++it)
{
if(it->GetText() == "")
continue;
std::string s = it->GetText();
if(ConfigUtils::IsDirectory(setting) && s[s.size()-1] != '/')
s += '/';
strings.push_back(s);
}
return cache.vecStrings.emplace(sSetting, strings).first->second;
}
std::vector<std::string> ConfigFile::GetSetting(ConfigSetting setting)
{
if(ConfigUtils::IsStringSetting(setting))
return {GetSettingString(setting)};
else if(ConfigUtils::IsVectorSetting(setting))
return GetSettingVectorString(setting);
else if(ConfigUtils::IsBoolSetting(setting))
return {GetSettingBool(setting) ? "true" : "false"};
else
{
LOG_ERROR("Invalid config setting");
return {};
}
}
bool ConfigFile::SetSettingString(ConfigSetting setting, const std::string& value)
{
// Check if valid enum
std::string s = value;
std::string sSetting = ConfigUtils::GetSettingName(setting);
if(ConfigUtils::IsStringSetting(setting))
{
if(ConfigUtils::IsDirectory(setting) && s[s.size()-1] != '/')
{
s += '/';
}
auto it = cache.strings.find(sSetting);
// Update cache
if(it != cache.strings.end())
it->second = s;
else
cache.strings.emplace(sSetting, s);
}
else if(ConfigUtils::IsBoolSetting(setting))
{
if(s == "true" || s == "t" || s == "yes" || s == "y")
s = "true";
else if(s == "false" || s == "f" || s == "no" || s == "n")
s = "false";
else
{
LOG_ERROR("Invalid boolean value: ", s);
return false;
}
auto it = cache.bools.find(sSetting);
// Update cache
if(it != cache.bools.end())
it->second = s == "true";
else
cache.bools.emplace(sSetting, value == "true");
}
else
{
LOG_ERROR("Not a string setting");
return false;
}
XMLObject& configuration = GetConfiguration();
std::vector<XMLObject>* values = configuration.GetObjectPtr(sSetting);
if(values == nullptr)
configuration.AddXMLObject({sSetting, {}, s});
else if(values->size() > 1)
LOG_ERROR("Multiple values of setting, changing first: ", sSetting, "=", s);
else
(*values)[0].SetText(s);
return true;
}
bool ConfigFile::AddSettingVectorString(ConfigSetting setting, const std::string& value)
{
// Check if valid enum
if(ConfigUtils::IsVectorSetting(setting))
{
std::string s = value;
if(ConfigUtils::IsDirectory(setting) && s[s.size()-1] != '/')
{
s += '/';
}
std::string sSetting = ConfigUtils::GetSettingName(setting);
auto it = cache.vecStrings.find(sSetting);
// Update cache
if(it != cache.vecStrings.end())
it->second.push_back(s);
else
cache.vecStrings.emplace(sSetting, std::vector<std::string>{s});
GetConfiguration().AddXMLObject({sSetting, {}, s});
}
else
{
LOG_ERROR("Not a vector setting");
return false;
}
return true;
}
bool ConfigFile::RemoveSettingVectorString(ConfigSetting setting, const std::string& value)
{
// Check if valid enum
if(ConfigUtils::IsVectorSetting(setting))
{
std::string s = value;
if(ConfigUtils::IsDirectory(setting) && s[s.size()-1] != '/')
{
s += '/';
}
std::string sSetting = ConfigUtils::GetSettingName(setting);
auto it = cache.vecStrings.find(sSetting);
if(it != cache.vecStrings.end())
{
// Update cache
for(auto itVec = it->second.begin(); itVec != it->second.end(); ++itVec)
{
if(*itVec == s)
{
it->second.erase(itVec);
}
}
}
std::vector<XMLObject>* values = GetConfiguration().GetObjectPtr(sSetting);
bool found = false;
for(auto it = values->begin(); it != values->end();++it)
{
if(it->GetText() == s)
{
values->erase(it);
found = true;
break;
}
}
if(!found)
{
LOG_ERROR("Couldn't find value: ", s);
return false;
}
}
else
{
LOG_ERROR("Not a vector setting");
return false;
}
return false;
}
XMLObject& ConfigFile::GetConfiguration()
{
std::vector<XMLObject>* configurations = config.GetObjectPtr("configuration");
if(configurations == nullptr || configurations->size() == 0)
{
LOG_ERROR("No configuration in makegen.xml");
assert(false);
}
for(auto it = configurations->begin(); it != configurations->end(); ++it)
{
if(!it->HasAttribute("name"))
{
LOG_ERROR("No name attribute in configuration tag");
continue;
}
if(it->GetAttribute("name") == target)
{
return *it;
}
}
LOG_ERROR("Couldn\'t find given target in config file. Using target=", (*configurations)[0].HasAttribute("name") ? (*configurations)[0].GetAttribute("name") : "");
return (*configurations)[0];
}
const std::string& ConfigFile::GetConfigPath() const
{
return configPath;;
}
ConfigFile& ConfigFile::GetDependencyConfig(size_t i)
{
return dependencyConfigs[i];
} }
std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath) std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath)
@@ -55,27 +339,39 @@ std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath,
return {}; return {};
} }
bool oldFile = false;
std::ifstream f(filepath + CONFIG_FILENAME); std::ifstream f(filepath + CONFIG_FILENAME);
if(!f.good())
{
ConfigFileConf::CreateXMLFile(realPath);
// try to read an old config file
f.close();
f = std::ifstream(filepath + CONFIG_FILENAME);
}
// Check if the file exists // Check if the file exists
if(f.good()) if(f.good())
{ {
f.close(); f.close();
ConfigFile conf = ConfigFile::Load(realPath); ConfigFile conf = ConfigFile(filepath);
if(conf.hasInitError)
return {};
loadedConfigs.emplace(realPath, conf); loadedConfigs.emplace(realPath, conf);
std::vector<std::string>& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency);
// Create dependency config files. // Create dependency config files.
for(size_t i = 0; i < conf.dependencies.size();++i) for(size_t i = 0; i < dependencies.size();++i)
{ {
std::optional<ConfigFile> dep = GetConfigFile(conf.configPath + conf.dependencies[i], loadedConfigs); std::optional<ConfigFile> dep = GetConfigFile(conf.configPath + dependencies[i], loadedConfigs);
if(dep) if(dep)
{ {
conf.dependencyConfigs.push_back(*dep); conf.dependencyConfigs.push_back(*dep);
conf.dependencies[i] = dep->configPath; dependencies[i] = dep->configPath;
} }
else else
{ {
// Remove the dependency since it is already accounted for // Remove the dependency since it is already accounted for
conf.dependencies.erase(conf.dependencies.begin() + i); dependencies.erase(dependencies.begin() + i);
--i; --i;
} }
} }
@@ -84,124 +380,6 @@ std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath,
return {}; return {};
} }
ConfigFile ConfigFile::Load(const std::string& filepath)
{
ConfigFile conf;
conf.configPath = filepath;
unsigned int loadFlag = 0;
std::vector<std::string>* vec;
std::string* s;
bool* b;
bool isDirectory = false;
std::ifstream file(filepath+CONFIG_FILENAME);
std::string line;
if(file.is_open())
{
// config name, { pointer to memory, isDirectory}
std::map<std::string, std::pair<std::string*, bool>> strings =
{
{"#srcdir", {&conf.srcdir, true}},
{"#outputdir", {&conf.outputdir, true}},
{"#outputname", {&conf.outputname, false}},
{"#projectname", {&conf.projectname, false}},
{"#hfile", {&conf.hFile, false}},
};
// config name, { pointer to memory, isDirectory}
std::map<std::string, std::pair<std::vector<std::string>*, bool>> vectors =
{
{"#libs", {&conf.libs, false}},
{"#libdirs", {&conf.libdirs, true}},
{"#includedirs", {&conf.includedirs, true}},
{"#compileflags", {&conf.flags, false}},
{"#defines", {&conf.defines, false}},
{"#dependencies", {&conf.dependencies, true}},
};
std::map<std::string, bool*> booleans =
{
{"#executable", &conf.executable},
{"#shared", &conf.shared},
{"#generatehfile", &conf.generateHFile},
};
while(std::getline(file,line))
{
if(line == "")
continue;
if(line[0]=='#')
{
// The format is a bit wacky, but it is this way since we do not want
// to use map::find for all maps. This way we gain some optimization.
auto&& itStr{strings.find(line)};
if(itStr != strings.end())
{
s = itStr->second.first;
isDirectory = itStr->second.second;
loadFlag = FLAG_STRING;
}
else
{
auto&& itVec{vectors.find(line)};
if(itVec != vectors.end())
{
vec = itVec->second.first;
isDirectory = itVec->second.second;
loadFlag = FLAG_VECTOR;
}
else
{
auto&& itBool{booleans.find(line)};
if(itBool != booleans.end())
{
b = itBool->second;
loadFlag = FLAG_BOOL;
}
else
{
LOG_ERROR("Invalid flag: ", line);
loadFlag = FLAG_NONE;
}
}
}
}
else
{
if(loadFlag == FLAG_STRING)
{
if(isDirectory && line[line.size()-1] != '/')
line += '/';
*s = line;
}
else if(loadFlag == FLAG_VECTOR)
{
if(isDirectory && line[line.size()-1] != '/')
{;
line += '/';
}
vec->push_back(line);
}
else if(loadFlag == FLAG_BOOL)
{
if(line == "true")
*b = true;
else
*b = false;
}
}
}
}
if(conf.hFile == "")
conf.hFile = conf.projectname+".h";
return conf;
}
void ConfigFile::InputBoolean(const std::string& inputText, bool& b) void ConfigFile::InputBoolean(const std::string& inputText, bool& b)
{ {
std::string input; std::string input;
@@ -248,90 +426,74 @@ void ConfigFile::InputMultiple(const std::string& inputText, std::vector<std::st
ConfigFile ConfigFile::Gen() ConfigFile ConfigFile::Gen()
{ {
ConfigFile conf; bool executable, shared, generateHFile;
InputBoolean("Should it be compiled as an executable? (y/n)", conf.executable); std::vector<std::string> libs, libdirs, includedirs, defines, compileFlags, dependencies;
std::string srcdir, outputdir, projectname, outputname, hFile;
InputBoolean("Should it be compiled as an executable? (y/n)", executable);
// If it isn't an executable there is not need to have libraries // If it isn't an executable there is not need to have libraries
if(conf.executable) if(executable)
{ {
InputMultiple("Enter library:", conf.libs,false); InputMultiple("Enter library:", libs,false);
InputMultiple("Enter library directory:", conf.libdirs,true); InputMultiple("Enter library directory:", libdirs,true);
InputMultiple("Enter project dependencies:", conf.dependencies,true); InputMultiple("Enter project dependencies:", dependencies,true);
} }
else else
{ {
InputBoolean("Should it be compiled as a shared library? (y/n)", conf.shared); InputBoolean("Should it be compiled as a shared library? (y/n)", shared);
InputBoolean("Should it compile a project h-file? (y/n):", conf.generateHFile); InputBoolean("Should it compile a project h-file? (y/n):", generateHFile);
if(conf.generateHFile) if(generateHFile)
{ {
InputString("Enter the project h-file name (relative to source directory): ", conf.hFile, false, false); InputString("Enter the project h-file name (relative to source directory): ", hFile, false, false);
} }
} }
InputMultiple("Enter include directory:", conf.includedirs, true); InputMultiple("Enter include directory:", includedirs, true);
InputString("Enter source directories:", conf.srcdir, true, false); InputString("Enter source directories:", srcdir, true, false);
InputMultiple("Enter preprocessor definitions:", conf.defines, false); InputMultiple("Enter preprocessor definitions:", defines, false);
InputMultiple("Enter compile flags:", conf.flags, false); InputMultiple("Enter compile flags:", compileFlags, false);
InputString("Enter output directory (default: bin):", conf.outputdir, true, true); InputString("Enter output directory (default: bin):", outputdir, true, true);
if(conf.outputdir == "") if(outputdir == "")
conf.outputdir = "bin/"; outputdir = "bin/";
InputString("Enter a name for the project:", conf.projectname, false, false); InputString("Enter a name for the project:", projectname, false, false);
InputString("Enter a name for the output file:", conf.outputname, false, false); InputString("Enter a name for the output file:", outputname, false, false);
return conf;
// Create xml
XMLObject makegen("makegen", {}, std::map<std::string, std::vector<XMLObject>>{});
// Version, target and configuration is probably going to be used in the future
makegen.AddXMLObject(XMLObject("version", {}, "v1.3.0"));
makegen.AddXMLObject(XMLObject("target", {}, "Release"));
XMLObject configuration("configuration", {{"name", "Release"}}, std::map<std::string, std::vector<XMLObject>>{});
configuration.AddXMLObject(XMLObject("projectname", {}, projectname));
configuration.AddXMLObject(XMLObject("outputname", {}, outputname));
configuration.AddXMLObject(XMLObject("srcdir", {}, srcdir));
configuration.AddXMLObject(XMLObject("outputdir", {}, outputdir));
configuration.AddXMLObject(XMLObject("hfilename", {}, hFile));
configuration.AddXMLObject(XMLObject("outputtype", {},
executable ? "executable" : (shared ? "sharedlibrary" : "staticlibrary")));
configuration.AddXMLObject(XMLObject("generatehfile", {}, generateHFile ? "true" : "false"));
for(auto it = libs.begin();it != libs.end(); ++it)
configuration.AddXMLObject({"library",{},*it});
for(auto it = libdirs.begin();it != libdirs.end(); ++it)
configuration.AddXMLObject({"librarydir",{},*it});
for(auto it = includedirs.begin();it != includedirs.end(); ++it)
configuration.AddXMLObject({"includedir",{},*it});
for(auto it = defines.begin();it != defines.end(); ++it)
configuration.AddXMLObject({"define",{},*it});
for(auto it = compileFlags.begin();it != compileFlags.end(); ++it)
configuration.AddXMLObject({"cflag",{},*it});
for(auto it = dependencies.begin();it != dependencies.end(); ++it)
configuration.AddXMLObject({"dependency",{},*it});
makegen.AddXMLObject(configuration);
return ConfigFile{makegen, FileUtils::GetRealPath("./")};
} }
void ConfigFile::Save() const void ConfigFile::Save() const
{ {
std::ofstream file("makegen.conf"); std::ofstream file("makegen.xml");
file << "#libs" << std::endl; file << config;
for(auto it = libs.begin();it!=libs.end();++it)
{
file << *it << std::endl;
}
file << "#libdirs" << std::endl;
for(auto it = libdirs.begin();it!=libdirs.end();++it)
{
file << *it << std::endl;
}
file << "#includedirs" << std::endl;
for(auto it = includedirs.begin();it!=includedirs.end();++it)
{
file << *it << std::endl;
}
file << "#defines" << std::endl;
for(auto it = defines.begin();it!=defines.end();++it)
{
file << *it << std::endl;
}
file << "#compileflags" << std::endl;
for(auto it = flags.begin();it!=flags.end();++it)
{
file << *it << std::endl;
}
file << "#dependencies" << std::endl;
for(auto it = dependencies.begin();it!=dependencies.end();++it)
{
file << *it << std::endl;
}
file << "#srcdir" << std::endl;
file << srcdir << std::endl;
file << "#outputdir" << std::endl;
file << outputdir << std::endl;
file << "#projectname" << std::endl;
file << projectname << std::endl;
file << "#outputname" << std::endl;
file << outputname << std::endl;
file << "#executable" << std::endl;
file << (executable ? "true" : "false") << std::endl;
file << "#generatehfile" << std::endl;
file << (generateHFile ? "true" : "false") << std::endl;
if(generateHFile)
{
file << "#hfile" << std::endl;
file << hFile << std::endl;
}
if(!executable)
{
file << "#shared" << std::endl;
file << (shared ? "true" : "false") << std::endl;
}
file.close();
} }
+37 -19
View File
@@ -1,40 +1,58 @@
#pragma once #pragma once
#include "ConfigUtils.h"
#include "xml/XMLObject.h"
#include <map> #include <map>
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
static const std::string CONFIG_FILENAME = "makegen.conf"; static const std::string CONFIG_FILENAME = "makegen.xml";
class ConfigFile class ConfigFile
{ {
public: private:
std::string configPath; ConfigCache cache;
std::vector<std::string> libs;
std::vector<std::string> libdirs;
std::vector<std::string> includedirs;
std::vector<std::string> defines;
std::vector<std::string> flags;
std::vector<std::string> dependencies;
std::string outputdir; XMLObject config;
std::string srcdir; // Current configuration
std::string outputname; std::string target;
std::string projectname;
std::string hFile; std::string configPath;
bool executable;
bool shared;
bool generateHFile;
std::vector<ConfigFile> dependencyConfigs; std::vector<ConfigFile> dependencyConfigs;
bool hasInitError = false;
public: public:
ConfigFile(); // Generates a new default config file
ConfigFile(const std::string& path, int);
ConfigFile(const std::string& path);
ConfigFile(XMLObject& config, const std::string& path);
void Save() const; void Save() const;
std::string& GetSettingString(ConfigSetting setting);
bool GetSettingBool(ConfigSetting setting);
std::vector<std::string>& GetSettingVectorString(ConfigSetting setting);
std::vector<std::string> GetSetting(ConfigSetting setting);
bool SetSettingString(ConfigSetting setting, const std::string& value);
bool AddSettingVectorString(ConfigSetting setting, const std::string& value);
bool RemoveSettingVectorString(ConfigSetting setting, const std::string& value);
XMLObject& GetConfiguration();
const std::string& GetConfigPath() const;
ConfigFile& GetDependencyConfig(size_t i);
private:
void Init();
public:
static ConfigFile Gen(); static ConfigFile Gen();
static std::optional<ConfigFile> GetConfigFile(const std::string& filepath = "./"); static std::optional<ConfigFile> GetConfigFile(const std::string& filepath = "./");
private: private:
static std::optional<ConfigFile> GetConfigFile(const std::string& filepath, std::map<std::string, ConfigFile>& loadedConfigs); static std::optional<ConfigFile> GetConfigFile(const std::string& filepath, std::map<std::string, ConfigFile>& loadedConfigs);
static ConfigFile Load(const std::string& filename); static std::optional<ConfigFile> Load(const std::string& filename);
static void InputBoolean(const std::string& inputText, bool& b); static void InputBoolean(const std::string& inputText, bool& b);
static void InputMultiple(const std::string& inputText, std::vector<std::string>& vec, bool needEnding); static void InputMultiple(const std::string& inputText, std::vector<std::string>& vec, bool needEnding);
static void InputString(const std::string& inputText, std::string& vec, bool needEnding, bool allowEmpty); static void InputString(const std::string& inputText, std::string& vec, bool needEnding, bool allowEmpty);
+242
View File
@@ -0,0 +1,242 @@
#pragma once
#include "Common.h"
#include "FileUtils.h"
#include <assert.h>
#include <map>
#include <vector>
#include <string>
struct ConfigCache
{
std::map<std::string, std::string> strings;
std::map<std::string, std::vector<std::string>> vecStrings;
std::map<std::string, bool> bools;
};
enum class ConfigSetting
{
// vectors
Library = 0, LibraryDir = 1, IncludeDir = 2, Define = 3, CFlag = 4, Dependency = 5,
// Strings
SourceDir = 32, OutputDir = 33, OutputName = 34, OutputType = 35, ProjectName = 36, HFileName = 37,
// Bools
GenerateHFile = 64,
// Other
Invalid = 1024
};
struct ConfigUtils
{
static std::string GetSettingName(ConfigSetting setting)
{
switch(setting)
{
case ConfigSetting::SourceDir:
return "srcdir";
case ConfigSetting::OutputDir:
return "outputdir";
case ConfigSetting::OutputName:
return "outputname";
case ConfigSetting::OutputType:
return "outputtype";
case ConfigSetting::ProjectName:
return "projectname";
case ConfigSetting::HFileName:
return "hfilename";
case ConfigSetting::LibraryDir:
return "librarydir";
case ConfigSetting::IncludeDir:
return "includedir";
case ConfigSetting::Dependency:
return "dependency";
case ConfigSetting::Library:
return "library";
case ConfigSetting::Define:
return "define";
case ConfigSetting::CFlag:
return "cflag";
case ConfigSetting::GenerateHFile:
return "generatehfile";
case ConfigSetting::Invalid:
return "invalid";
}
}
static bool IsDirectory(ConfigSetting setting)
{
// Library, LibraryDir, IncludeDir, Define, CFlag, Dependency,
switch(setting)
{
case ConfigSetting::SourceDir:
case ConfigSetting::OutputDir:
case ConfigSetting::LibraryDir:
case ConfigSetting::IncludeDir:
case ConfigSetting::Dependency:
return true;
case ConfigSetting::OutputName:
case ConfigSetting::OutputType:
case ConfigSetting::ProjectName:
case ConfigSetting::HFileName:
case ConfigSetting::Library:
case ConfigSetting::Define:
case ConfigSetting::CFlag:
case ConfigSetting::GenerateHFile:
return false;
default:
LOG_ERROR("INVALID ENUM: ", (int)setting);
assert(false);
}
}
static bool IsStringSetting(ConfigSetting setting)
{
switch(setting)
{
case ConfigSetting::SourceDir:
case ConfigSetting::OutputDir:
case ConfigSetting::OutputName:
case ConfigSetting::OutputType:
case ConfigSetting::ProjectName:
case ConfigSetting::HFileName:
return true;
case ConfigSetting::LibraryDir:
case ConfigSetting::IncludeDir:
case ConfigSetting::Dependency:
case ConfigSetting::Library:
case ConfigSetting::Define:
case ConfigSetting::CFlag:
case ConfigSetting::GenerateHFile:
case ConfigSetting::Invalid:
return false;
}
}
static bool IsVectorSetting(ConfigSetting setting)
{
switch(setting)
{
case ConfigSetting::LibraryDir:
case ConfigSetting::IncludeDir:
case ConfigSetting::Dependency:
case ConfigSetting::Library:
case ConfigSetting::Define:
case ConfigSetting::CFlag:
return true;
case ConfigSetting::SourceDir:
case ConfigSetting::OutputDir:
case ConfigSetting::OutputName:
case ConfigSetting::OutputType:
case ConfigSetting::ProjectName:
case ConfigSetting::HFileName:
case ConfigSetting::GenerateHFile:
case ConfigSetting::Invalid:
return false;
}
}
static bool IsBoolSetting(ConfigSetting setting)
{
switch(setting)
{
case ConfigSetting::GenerateHFile:
return true;
case ConfigSetting::SourceDir:
case ConfigSetting::OutputDir:
case ConfigSetting::OutputName:
case ConfigSetting::OutputType:
case ConfigSetting::ProjectName:
case ConfigSetting::HFileName:
case ConfigSetting::LibraryDir:
case ConfigSetting::IncludeDir:
case ConfigSetting::Dependency:
case ConfigSetting::Library:
case ConfigSetting::Define:
case ConfigSetting::CFlag:
case ConfigSetting::Invalid:
return false;
}
}
static std::string GetDefaultSettingString(ConfigSetting setting, const std::string& path)
{
switch(setting)
{
case ConfigSetting::SourceDir:
return "src/";
case ConfigSetting::OutputDir:
return "bin/";
case ConfigSetting::OutputName:
return GetDefaultOutputName(path);
case ConfigSetting::OutputType:
return "executable";
case ConfigSetting::ProjectName:
return GetDefaultProjectName(path);
case ConfigSetting::HFileName:
return GetDefaultHFileName(path);
case ConfigSetting::GenerateHFile:
return GetDefaultSettingBool(setting) ? "true" : "false";
default:
LOG_ERROR("INVALID STRING ENUM: ", (int)setting);
assert(false);
}
}
static bool GetDefaultSettingBool(ConfigSetting setting)
{
switch(setting)
{
case ConfigSetting::GenerateHFile:
return false;
default:
LOG_ERROR("NOT BOOLEAN VALUE: ", (int)setting);
assert(false);
}
}
static std::string GetDefaultProjectName(const std::string& path)
{
return FileUtils::GetTopDirectory(path);
}
static std::string GetDefaultOutputName(const std::string& path)
{
std::string projectname = GetDefaultProjectName(path);
std::string outputname;
std::transform(
projectname.begin(),
projectname.end(),
std::back_inserter(outputname),
[](unsigned char c)
{
if(c == ' ')
return '_';
return (char)std::tolower(c);
});
auto it = std::remove_if(
outputname.begin(),
outputname.end(),
[](unsigned char c)
{
return (c < '0' || c > '9') && (c < 'a' || c > 'z') && c != '_';
});
outputname.erase(it, outputname.end());
outputname += ".out";
return outputname;
}
static std::string GetDefaultHFileName(const std::string& path)
{
std::string hfile = GetDefaultProjectName(path);
auto it = std::remove_if(
hfile.begin(),
hfile.end(),
[](unsigned char c)
{
return (c < 'a' || c > 'z') && (c < 'A' || c > 'Z');
});
hfile.erase(it, hfile.end());
hfile += ".h";
return hfile;
}
};
+15 -3
View File
@@ -34,14 +34,26 @@ struct FileUtils
{ {
static char path[256]; // Usual maximum filename static char path[256]; // Usual maximum filename
getcwd(path, sizeof(path)); getcwd(path, sizeof(path));
std::string dir = path; return GetTopDirectory(path);
size_t pos = dir.find_last_of("/"); }
static std::string GetTopDirectory(const std::string& dir)
{
if(dir.size() == 0)
{
LOG_ERROR("Cannot send empty string to FileUtils::GetTopDirectory()");
assert(false);
}
size_t dirEnd = std::string::npos;
if(dir[dir.size()-1] == '/')
dirEnd = dir.size()-2;
size_t pos = dir.find_last_of("/", dirEnd);
if(pos == std::string::npos) if(pos == std::string::npos)
{ {
LOG_ERROR("Couldn't find / (slash) in directory. This shouldn't occur."); LOG_ERROR("Couldn't find / (slash) in directory. This shouldn't occur.");
assert(false); assert(false);
} }
return dir.substr(pos+1); return dir.substr(pos + 1, dirEnd - pos);
} }
static std::string GetRealPath(const std::string& filename) static std::string GetRealPath(const std::string& filename)
+4 -4
View File
@@ -3,11 +3,11 @@
#include "FileUtils.h" #include "FileUtils.h"
#include <set> #include <set>
void HFileGen::Create(const ConfigFile& conf) void HFileGen::Create(ConfigFile& conf)
{ {
std::set<std::string> hFiles; std::set<std::string> hFiles;
std::vector<std::string> files; std::vector<std::string> files;
std::string path = conf.configPath + conf.srcdir; std::string path = conf.GetConfigPath() + conf.GetSettingString(ConfigSetting::SourceDir);
FileUtils::GetAllFiles(path,files); FileUtils::GetAllFiles(path,files);
// include paramenter with the path of the file // include paramenter with the path of the file
// For example src/graphics/Window.h -> graphics/Window.h if src is a src folder // For example src/graphics/Window.h -> graphics/Window.h if src is a src folder
@@ -17,7 +17,7 @@ void HFileGen::Create(const ConfigFile& conf)
if(extensionPos != std::string::npos) if(extensionPos != std::string::npos)
{ {
std::string filename = it->substr(path.length()); std::string filename = it->substr(path.length());
if(it->substr(extensionPos+1) == "h" && filename != conf.configPath + conf.hFile) if(it->substr(extensionPos+1) == "h" && filename != conf.GetConfigPath() + conf.GetSettingString(ConfigSetting::HFileName))
{ {
// Make files sorted in alphabetical order // Make files sorted in alphabetical order
hFiles.emplace(filename); hFiles.emplace(filename);
@@ -25,7 +25,7 @@ void HFileGen::Create(const ConfigFile& conf)
} }
} }
std::ofstream os(conf.configPath + conf.srcdir+"/"+conf.hFile); std::ofstream os(path + "/" + conf.GetSettingString(ConfigSetting::HFileName));
os << "#pragma once" << std::endl << std::endl; os << "#pragma once" << std::endl << std::endl;
for(auto&& hFile : hFiles) for(auto&& hFile : hFiles)
os << "#include <" << hFile << ">" << std::endl; os << "#include <" << hFile << ">" << std::endl;
+1 -1
View File
@@ -5,5 +5,5 @@
class HFileGen class HFileGen
{ {
public: public:
static void Create(const ConfigFile& conf); static void Create(ConfigFile& conf);
}; };
+1 -1
View File
@@ -32,7 +32,7 @@ class IncludeDeps
printCounter++; printCounter++;
printSet.emplace(filepath); printSet.emplace(filepath);
if(!projectHFile) if(!projectHFile)
stream << FileUtils::GetRelativePath(conf.configPath, filepath); stream << FileUtils::GetRelativePath(conf.GetConfigPath(), filepath);
for(auto it = dependencies.begin();it!=dependencies.end();++it) for(auto it = dependencies.begin();it!=dependencies.end();++it)
{ {
stream << " "; stream << " ";
+30 -22
View File
@@ -6,7 +6,7 @@
#include <fstream> #include <fstream>
#include <map> #include <map>
void Makefile::Save(const ConfigFile& conf, unsigned int flags) void Makefile::Save(ConfigFile& conf, unsigned int flags)
{ {
std::set<HFile> hFiles; // hFile, directory std::set<HFile> hFiles; // hFile, directory
std::set<std::string> cppFiles; std::set<std::string> cppFiles;
@@ -15,14 +15,15 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
else else
Utils::GetCppAndHFiles(conf, hFiles, cppFiles); Utils::GetCppAndHFiles(conf, hFiles, cppFiles);
std::ofstream outputFile(conf.configPath + "Makefile"); std::ofstream outputFile(conf.GetConfigPath()+ "Makefile");
outputFile << "# This Makefile was generated using MakeGen "<< MAKEGEN_VERSION << " made by Tim Håkansson" << std::endl; outputFile << "# This Makefile was generated using MakeGen "<< MAKEGEN_VERSION << " made by Tim Håkansson" << std::endl;
outputFile << "# and is licensed under MIT. Full source of the project can be found at" << std::endl; outputFile << "# and is licensed under MIT. Full source of the project can be found at" << std::endl;
outputFile << "# https://github.com/Thraix/MakeGen" << std::endl; outputFile << "# https://github.com/Thraix/MakeGen" << std::endl;
outputFile << "CC=@g++" << std::endl; outputFile << "CC=@g++" << std::endl;
if(!conf.executable) std::string outputtype = conf.GetSettingString(ConfigSetting::OutputType);
if(outputtype != "executable")
{ {
if(conf.shared) if(outputtype == "sharedlibrary")
outputFile << "CO=@g++ -shared -o" << std::endl; outputFile << "CO=@g++ -shared -o" << std::endl;
else else
outputFile << "CO=@g++ -o" << std::endl; outputFile << "CO=@g++ -o" << std::endl;
@@ -31,10 +32,11 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
outputFile << "CO=@g++ -o" << std::endl; outputFile << "CO=@g++ -o" << std::endl;
outputFile << "MKDIR_P=mkdir -p" << std::endl; outputFile << "MKDIR_P=mkdir -p" << std::endl;
outputFile << "BIN=" << conf.outputdir << std::endl; outputFile << "BIN=" << conf.GetSettingString(ConfigSetting::OutputDir) << std::endl;
outputFile << "OBJPATH=$(BIN)intermediates" << std::endl; outputFile << "OBJPATH=$(BIN)intermediates" << std::endl;
outputFile << "INCLUDES="; outputFile << "INCLUDES=";
for(auto it = conf.includedirs.begin();it!=conf.includedirs.end();++it) std::vector<std::string>& includedirs = conf.GetSettingVectorString(ConfigSetting::IncludeDir);
for(auto it = includedirs.begin(); it != includedirs.end(); ++it)
{ {
outputFile << "-I./" << *it << " "; outputFile << "-I./" << *it << " ";
} }
@@ -47,7 +49,7 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
outputFile << "$(OBJPATH)/" << it->substr(slash, extensionPos - slash) << ".o "; outputFile << "$(OBJPATH)/" << it->substr(slash, extensionPos - slash) << ".o ";
} }
outputFile << std::endl; outputFile << std::endl;
if(conf.executable || !conf.shared) if(outputtype == "executable" || outputtype != "sharedlibrary")
{ {
outputFile << "CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3 "; outputFile << "CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3 ";
} }
@@ -55,46 +57,51 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
{ {
outputFile << "CFLAGS=$(INCLUDES) -fPIC -std=c++17 -c -w -g3 "; outputFile << "CFLAGS=$(INCLUDES) -fPIC -std=c++17 -c -w -g3 ";
} }
for(auto it = conf.defines.begin();it!=conf.defines.end();++it) std::vector<std::string>& defines = conf.GetSettingVectorString(ConfigSetting::Define);
for(auto it = defines.begin(); it != defines.end(); ++it)
{ {
outputFile << "-D" << *it << " "; outputFile << "-D" << *it << " ";
} }
for(auto it = conf.flags.begin();it!=conf.flags.end();++it) std::vector<std::string>& cflags = conf.GetSettingVectorString(ConfigSetting::CFlag);
for(auto it = cflags.begin(); it != cflags.end(); ++it)
{ {
outputFile << *it << " "; outputFile << *it << " ";
} }
outputFile << std::endl; outputFile << std::endl;
if(conf.executable) if(outputtype == "executable")
{ {
std::vector<std::string>& libdirs= conf.GetSettingVectorString(ConfigSetting::LibraryDir);
outputFile << "LIBDIR="; outputFile << "LIBDIR=";
for(auto it = conf.libdirs.begin();it!=conf.libdirs.end();++it) for(auto it = libdirs.begin();it!=libdirs.end();++it)
{ {
outputFile << "-L./" << *it << " "; outputFile << "-L./" << *it << " ";
} }
outputFile << std::endl; outputFile << std::endl;
outputFile << "LDFLAGS="; outputFile << "LDFLAGS=";
for(auto it = conf.libdirs.begin();it!=conf.libdirs.end();++it) for(auto it = libdirs.begin(); it != libdirs.end(); ++it)
{ {
outputFile << "-Wl,-rpath=" << *it << " "; outputFile << "-Wl,-rpath=" << *it << " ";
} }
outputFile << std::endl; outputFile << std::endl;
std::vector<std::string>& libs = conf.GetSettingVectorString(ConfigSetting::Library);
outputFile << "LIBS=$(LIBDIR) "; outputFile << "LIBS=$(LIBDIR) ";
for(auto it = conf.libs.begin();it!=conf.libs.end();++it) for(auto it = libs.begin(); it != libs.end(); ++it)
{ {
outputFile << "-l" << *it << " "; outputFile << "-l" << *it << " ";
} }
outputFile << std::endl; outputFile << std::endl;
if(!conf.dependencies.empty()) std::vector<std::string>& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency);
if(!dependencies.empty())
{ {
outputFile << "DEPENDENCIES="; outputFile << "DEPENDENCIES=";
for(auto it = conf.dependencies.begin();it!=conf.dependencies.end();++it) for(auto it = dependencies.begin();it!=dependencies.end();++it)
{ {
outputFile << *it << " "; outputFile << *it << " ";
} }
outputFile << std::endl; outputFile << std::endl;
} }
} }
outputFile << "OUTPUT=$(BIN)" << conf.outputname << std::endl; outputFile << "OUTPUT=$(BIN)" << conf.GetSettingString(ConfigSetting::OutputName) << std::endl;
outputFile << ".PHONY: all directories rebuild clean run" << std::endl; outputFile << ".PHONY: all directories rebuild clean run" << std::endl;
// All // All
@@ -114,7 +121,7 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
// Run // Run
outputFile << "run: all" << std::endl; outputFile << "run: all" << std::endl;
if(conf.executable) if(outputtype == "executable")
{ {
outputFile << "\t@./$(OUTPUT)" << std::endl; outputFile << "\t@./$(OUTPUT)" << std::endl;
} }
@@ -130,25 +137,26 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
// Output file // Output file
outputFile << "$(OUTPUT): $(OBJECTS)" << std::endl; outputFile << "$(OUTPUT): $(OBJECTS)" << std::endl;
outputFile << "\t$(info Generating output file)" << std::endl; outputFile << "\t$(info Generating output file)" << std::endl;
if(conf.executable) if(outputtype == "executable")
outputFile << "\t$(CO) $(OUTPUT) $(OBJECTS) $(LDFLAGS) $(LIBS)" << std::endl; outputFile << "\t$(CO) $(OUTPUT) $(OBJECTS) $(LDFLAGS) $(LIBS)" << std::endl;
else else
outputFile << "\t$(CO) $(OUTPUT) $(OBJECTS)" << std::endl; outputFile << "\t$(CO) $(OUTPUT) $(OBJECTS)" << std::endl;
// Install // Install
outputFile << "install: all" << std::endl; outputFile << "install: all" << std::endl;
outputFile << "\t$(info Installing " << conf.projectname <<" to /usr/bin/)" << std::endl; outputFile << "\t$(info Installing " << conf.GetSettingString(ConfigSetting::ProjectName) <<" to /usr/bin/)" << std::endl;
outputFile << "\t@cp $(OUTPUT) /usr/bin/" << conf.outputname << std::endl; outputFile << "\t@cp $(OUTPUT) /usr/bin/" << conf.GetSettingString(ConfigSetting::OutputName) << std::endl;
std::map<std::string, IncludeDeps*> dependencies; std::map<std::string, IncludeDeps*> dependencies;
size_t i = 0; size_t i = 0;
for(auto it = cppFiles.begin(); it != cppFiles.end();++it) for(auto it = cppFiles.begin(); it != cppFiles.end();++it)
{ {
i++; i++;
auto itD = dependencies.find(conf.srcdir + *it); std::string& srcdir = conf.GetSettingString(ConfigSetting::SourceDir);
auto itD = dependencies.find(srcdir + *it);
if(itD == dependencies.end()) if(itD == dependencies.end())
{ {
IncludeDeps* deps = new IncludeDeps(*it, conf.configPath+conf.srcdir,hFiles,dependencies); IncludeDeps* deps = new IncludeDeps(*it, conf.GetConfigPath() + srcdir,hFiles,dependencies);
size_t extensionPos = it->find_last_of("."); size_t extensionPos = it->find_last_of(".");
size_t slash = it->find_last_of("/")+1; size_t slash = it->find_last_of("/")+1;
std::string oFile = it->substr(slash, extensionPos - slash)+".o "; std::string oFile = it->substr(slash, extensionPos - slash)+".o ";
+1 -1
View File
@@ -5,5 +5,5 @@
class Makefile class Makefile
{ {
public: public:
static void Save(const ConfigFile& conf, unsigned int flags); static void Save(ConfigFile& conf, unsigned int flags);
}; };
+43 -11
View File
@@ -1,5 +1,6 @@
#include "Utils.h" #include "Utils.h"
#include "ConfigFile.h"
#include "FileUtils.h" #include "FileUtils.h"
std::string Utils::CommonPrefix(const std::string& s1, const std::string& s2) std::string Utils::CommonPrefix(const std::string& s1, const std::string& s2)
@@ -16,10 +17,10 @@ std::string Utils::CommonPrefix(const std::string& s1, const std::string& s2)
return s1.substr(0, n); return s1.substr(0, n);
} }
void Utils::GetCppFiles(const ConfigFile& conf, std::set<std::string>& cppFiles) void Utils::GetCppFiles(ConfigFile& conf, std::set<std::string>& cppFiles)
{ {
std::vector<std::string> files; std::vector<std::string> files;
std::string path = conf.configPath + conf.srcdir; std::string path = conf.GetConfigPath() + conf.GetSettingString(ConfigSetting::SourceDir);
FileUtils::GetAllFiles(path, files); FileUtils::GetAllFiles(path, files);
for(auto it = files.begin(); it!=files.end();++it) for(auto it = files.begin(); it!=files.end();++it)
@@ -37,10 +38,10 @@ void Utils::GetCppFiles(const ConfigFile& conf, std::set<std::string>& cppFiles)
} }
} }
void Utils::GetCppAndHFiles(const ConfigFile& conf, std::set<HFile>& hFiles, std::set<std::string>& cppFiles) void Utils::GetCppAndHFiles(ConfigFile& conf, std::set<HFile>& hFiles, std::set<std::string>& cppFiles)
{ {
std::vector<std::string> files; std::vector<std::string> files;
std::string path = conf.configPath + conf.srcdir; std::string path = conf.GetConfigPath() + conf.GetSettingString(ConfigSetting::SourceDir);
FileUtils::GetAllFiles(path,files); FileUtils::GetAllFiles(path,files);
// include paramenter with the path of the file // include paramenter with the path of the file
// For example src/graphics/Window.h -> graphics/Window.h if src is a src folder // For example src/graphics/Window.h -> graphics/Window.h if src is a src folder
@@ -62,24 +63,26 @@ void Utils::GetCppAndHFiles(const ConfigFile& conf, std::set<HFile>& hFiles, std
} }
} }
for(size_t i = 0; i < conf.dependencies.size(); ++i) std::vector<std::string>& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency);
for(size_t i = 0; i < dependencies.size(); ++i)
{ {
GetHFiles(conf.dependencies[i], conf.dependencyConfigs[i], hFiles); GetHFiles(dependencies[i], conf.GetDependencyConfig(i), hFiles);
} }
} }
void Utils::GetHFiles(const std::string& dependencyDir, const ConfigFile& conf, std::set<HFile>& hFiles) void Utils::GetHFiles(const std::string& dependencyDir, ConfigFile& conf, std::set<HFile>& hFiles)
{ {
// TODO: Fix so that cyclic dependencies doesn't crash the tool. // TODO: Fix so that cyclic dependencies doesn't crash the tool.
// Cyclic dependencies probably shouldn't exist. // Cyclic dependencies probably shouldn't exist.
// so just warn the user that it does and terminate. // so just warn the user that it does and terminate.
for(size_t i = 0; i < conf.dependencies.size(); ++i) std::vector<std::string>& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency);
for(size_t i = 0; i < dependencies.size(); ++i)
{ {
GetHFiles(conf.dependencies[i], conf.dependencyConfigs[i], hFiles); GetHFiles(dependencies[i], conf.GetDependencyConfig(i), hFiles);
} }
std::vector<std::string> files; std::vector<std::string> files;
std::string depSrcDir = dependencyDir + conf.srcdir; std::string depSrcDir = dependencyDir + conf.GetSettingString(ConfigSetting::SourceDir);
FileUtils::GetAllFiles(depSrcDir, files); FileUtils::GetAllFiles(depSrcDir, files);
for(auto it = files.begin(); it!=files.end();++it) for(auto it = files.begin(); it!=files.end();++it)
{ {
@@ -90,8 +93,37 @@ void Utils::GetHFiles(const std::string& dependencyDir, const ConfigFile& conf,
if(extension == "hpp" || extension == "h") if(extension == "hpp" || extension == "h")
{ {
std::string filename = it->substr(depSrcDir.length()); std::string filename = it->substr(depSrcDir.length());
hFiles.emplace(HFile{filename, depSrcDir, conf.generateHFile && filename == conf.hFile}); hFiles.emplace(HFile{filename, depSrcDir, conf.GetSettingBool(ConfigSetting::GenerateHFile) && filename == conf.GetSettingString(ConfigSetting::HFileName)});
} }
} }
} }
} }
bool Utils::IsWhiteSpace(char c)
{
return c == '\n' || c == '\t' || c == '\r' || c == ' ' || c == '\t';
}
bool Utils::IsLetter(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
bool Utils::IsWord(const std::string& string)
{
for(auto it{string.begin()}; it != string.end();++it)
{
if(!IsLetter(*it))
return false;
}
return true;
}
std::string Utils::GetWord(const std::string& string, int startPos)
{
if (startPos >= string.length())
throw std::runtime_error("start position out of bounds.");
int endPos = startPos;
while (endPos < string.length() && IsLetter(string[endPos])) endPos++;
return string.substr(startPos, endPos - startPos);
}
+11 -5
View File
@@ -1,7 +1,5 @@
#pragma once #pragma once
#include "ConfigFile.h"
#include <set> #include <set>
#include <string> #include <string>
@@ -24,10 +22,18 @@ struct HFile
} }
}; };
class ConfigFile;
struct Utils struct Utils
{ {
static std::string CommonPrefix(const std::string& s1, const std::string& s2); static std::string CommonPrefix(const std::string& s1, const std::string& s2);
static void GetCppFiles(const ConfigFile& conf, std::set<std::string>& cppFiles); static void GetCppFiles(ConfigFile& conf, std::set<std::string>& cppFiles);
static void GetCppAndHFiles(const ConfigFile& conf, std::set<HFile>& hFiles, std::set<std::string>& cppFiles); static void GetCppAndHFiles(ConfigFile& conf, std::set<HFile>& hFiles, std::set<std::string>& cppFiles);
static void GetHFiles(const std::string& dependencyDir, const ConfigFile& conf, std::set<HFile>& hFiles); static void GetHFiles(const std::string& dependencyDir, ConfigFile& conf, std::set<HFile>& hFiles);
// Used for parsing xml
static bool IsWhiteSpace(char c);
static bool IsLetter(char c);
static bool IsWord(const std::string& string);
static std::string GetWord(const std::string& string, int startPos = 0);
}; };
+194
View File
@@ -0,0 +1,194 @@
#include "ConfigFileConf.h"
const std::string CONFIG_FILENAME_CONF = "makegen.conf";
#include "../ConfigFile.h"
#include "../FileUtils.h"
#include <algorithm>
#include <fstream>
#define FLAG_NONE 0
#define FLAG_VECTOR 1
#define FLAG_STRING 2
#define FLAG_BOOL 3
ConfigFileConf::ConfigFileConf()
: outputdir("bin/"), srcdir("src/"), outputname(""), projectname(FileUtils::GetCurrentDirectory()), hFile(""), executable(true), shared(true), generateHFile(false)
{
// Converts project name (current directory) to lowercase
// and replace whitespace with underscore
std::transform(
projectname.begin(),
projectname.end(),
std::back_inserter(outputname),
[](unsigned char c)
{
if(c == ' ')
return '_';
return (char)std::tolower(c);
});
// Removes all other characters
std::remove_if(
outputdir.begin(),
outputdir.end(),
[](unsigned char c)
{
return (c < 'a' || c > 'z') && c != '_';
});
// Add suffix
outputname += ".out";
}
void ConfigFileConf::CreateXMLFile(const std::string& filepath)
{
ConfigFileConf conf;
conf.configPath = filepath;
unsigned int loadFlag = 0;
std::vector<std::string>* vec;
std::string* s;
bool* b;
bool isDirectory = false;
std::ifstream file(filepath + CONFIG_FILENAME_CONF);
std::string line;
if(file.is_open())
{
// config name, { pointer to memory, isDirectory}
std::map<std::string, std::pair<std::string*, bool>> strings =
{
{"#srcdir", {&conf.srcdir, true}},
{"#outputdir", {&conf.outputdir, true}},
{"#outputname", {&conf.outputname, false}},
{"#projectname", {&conf.projectname, false}},
{"#hfile", {&conf.hFile, false}},
};
// config name, { pointer to memory, isDirectory}
std::map<std::string, std::pair<std::vector<std::string>*, bool>> vectors =
{
{"#libs", {&conf.libs, false}},
{"#libdirs", {&conf.libdirs, true}},
{"#includedirs", {&conf.includedirs, true}},
{"#compileflags", {&conf.flags, false}},
{"#defines", {&conf.defines, false}},
{"#dependencies", {&conf.dependencies, true}},
};
std::map<std::string, bool*> booleans =
{
{"#executable", &conf.executable},
{"#shared", &conf.shared},
{"#generatehfile", &conf.generateHFile},
};
while(std::getline(file,line))
{
if(line == "")
continue;
if(line[0]=='#')
{
// The format is a bit wacky, but it is this way since we do not want
// to use map::find for all maps. This way we gain some optimization.
auto&& itStr{strings.find(line)};
if(itStr != strings.end())
{
s = itStr->second.first;
isDirectory = itStr->second.second;
loadFlag = FLAG_STRING;
}
else
{
auto&& itVec{vectors.find(line)};
if(itVec != vectors.end())
{
vec = itVec->second.first;
isDirectory = itVec->second.second;
loadFlag = FLAG_VECTOR;
}
else
{
auto&& itBool{booleans.find(line)};
if(itBool != booleans.end())
{
b = itBool->second;
loadFlag = FLAG_BOOL;
}
else
{
LOG_ERROR("Invalid flag: ", line);
loadFlag = FLAG_NONE;
}
}
}
}
else
{
if(loadFlag == FLAG_STRING)
{
if(isDirectory && line[line.size()-1] != '/')
line += '/';
*s = line;
}
else if(loadFlag == FLAG_VECTOR)
{
if(isDirectory && line[line.size()-1] != '/')
{;
line += '/';
}
vec->push_back(line);
}
else if(loadFlag == FLAG_BOOL)
{
if(line == "true")
*b = true;
else
*b = false;
}
}
}
LOG_INFO("------ COULDN\'T FIND makegen.xml. BUT FOUND OLD makegen.conf.");
LOG_INFO("------ GENERATING NEW CONFIGURATION FILE");
if(conf.hFile == "")
conf.hFile = conf.projectname+".h";
XMLObject makegen("makegen", {}, std::map<std::string, std::vector<XMLObject>>{});
// Version, target and configuration is probably going to be used in the future
makegen.AddXMLObject(XMLObject("version", {}, "v1.3.0"));
makegen.AddXMLObject(XMLObject("target", {}, "Release"));
XMLObject configuration("configuration", {{"name", "Release"}}, std::map<std::string, std::vector<XMLObject>>{});
configuration.AddXMLObject(XMLObject("projectname", {}, conf.projectname));
configuration.AddXMLObject(XMLObject("outputname", {}, conf.outputname));
configuration.AddXMLObject(XMLObject("srcdir", {}, conf.srcdir));
configuration.AddXMLObject(XMLObject("outputdir", {}, conf.outputdir));
configuration.AddXMLObject(XMLObject("hfilename", {}, conf.hFile));
configuration.AddXMLObject(XMLObject("outputtype", {},
conf.executable ? "executable" : (conf.shared ? "sharedlibrary" : "staticlibrary")));
configuration.AddXMLObject(XMLObject("generatehfile", {}, conf.generateHFile ? "true" : "false"));
for(auto it = conf.libs.begin();it != conf.libs.end(); ++it)
configuration.AddXMLObject({"library",{},*it});
for(auto it = conf.libdirs.begin();it != conf.libdirs.end(); ++it)
configuration.AddXMLObject({"librarydir",{},*it});
for(auto it = conf.includedirs.begin();it != conf.includedirs.end(); ++it)
configuration.AddXMLObject({"includedir",{},*it});
for(auto it = conf.defines.begin();it != conf.defines.end(); ++it)
configuration.AddXMLObject({"define",{},*it});
for(auto it = conf.flags.begin();it != conf.flags.end(); ++it)
configuration.AddXMLObject({"cflag",{},*it});
for(auto it = conf.dependencies.begin();it != conf.dependencies.end(); ++it)
configuration.AddXMLObject({"dependency",{},*it});
makegen.AddXMLObject(configuration);
std::ofstream xmlFile(conf.configPath + "makegen.xml");
xmlFile << makegen;
}
}
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <map>
#include <optional>
#include <string>
#include <vector>
class ConfigFile;
class ConfigFileConf
{
public:
std::string configPath;
std::vector<std::string> libs;
std::vector<std::string> libdirs;
std::vector<std::string> includedirs;
std::vector<std::string> defines;
std::vector<std::string> flags;
std::vector<std::string> dependencies;
std::string outputdir;
std::string srcdir;
std::string outputname;
std::string projectname;
std::string hFile;
bool executable;
bool shared;
bool generateHFile;
public:
ConfigFileConf();
static void CreateXMLFile(const std::string& filename);
private:
};
+16 -12
View File
@@ -47,9 +47,9 @@ Usage: makegen [options]
clean all install run, rebuild will be translated to \"clean make\")"); clean all install run, rebuild will be translated to \"clean make\")");
} }
void GenMakefile(const ConfigFile& conf, unsigned int flags) void GenMakefile(ConfigFile& conf, unsigned int flags)
{ {
if(conf.generateHFile) if(conf.GetSettingBool(ConfigSetting::GenerateHFile))
HFileGen::Create(conf); HFileGen::Create(conf);
Makefile::Save(conf, flags); Makefile::Save(conf, flags);
} }
@@ -115,7 +115,7 @@ unsigned int ReadFlags(int argc, char** argv)
return flags; return flags;
} }
bool RunMake(const std::string& filepath, unsigned int flags, const ConfigFile& conf) bool RunMake(const std::string& filepath, unsigned int flags, ConfigFile& conf)
{ {
std::string make = "make --no-print-directory -C " + filepath; std::string make = "make --no-print-directory -C " + filepath;
if(!(flags & FLAG_SINGLE_THREAD)) if(!(flags & FLAG_SINGLE_THREAD))
@@ -132,33 +132,37 @@ bool RunMake(const std::string& filepath, unsigned int flags, const ConfigFile&
{ {
RETURN_IF(system(std::string(make + " install").c_str()) != 0, false); RETURN_IF(system(std::string(make + " install").c_str()) != 0, false);
} }
if(flags & FLAG_RUN && conf.executable) if(flags & FLAG_RUN && conf.GetSettingString(ConfigSetting::OutputType) == "executable")
{ {
RETURN_IF(system(std::string(make + " run").c_str()) != 0, false); RETURN_IF(system(std::string(make + " run").c_str()) != 0, false);
} }
return true; return true;
} }
bool MakeGen(const std::string& filepath, unsigned int flags, const ConfigFile& conf) bool MakeGen(const std::string& filepath, unsigned int flags, ConfigFile& conf)
{ {
for(size_t i = 0;i<conf.dependencies.size();++i) std::vector<std::string>& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency);
for(size_t i = 0;i<dependencies.size();++i)
{ {
bool success = MakeGen(conf.dependencies[i], flags, conf.dependencyConfigs[i]); bool success = MakeGen(dependencies[i], flags, conf.GetDependencyConfig(i));
if(!success) if(!success)
return success; return success;
} }
LOG_INFO("-----------------------------------"); LOG_INFO("-----------------------------------");
LOG_INFO("Building ", conf.projectname); LOG_INFO("Building ", conf.GetSettingString(ConfigSetting::ProjectName));
LOG_INFO("Generating Makefile..."); LOG_INFO("Generating Makefile...");
Timer timer; Timer timer;
GenMakefile(conf, flags); GenMakefile(conf, flags);
LOG_INFO("Took ", round(timer.Elapsed()*1000.0)/1000.0,"s"); LOG_INFO("Took ", round(timer.Elapsed()*1000.0)/1000.0,"s");
LOG_INFO("Running Makefile..."); LOG_INFO("Running Makefile...");
if(!FileUtils::HasPath(conf.configPath + conf.outputdir)) std::string outputPath = conf.GetConfigPath() + conf.GetSettingString(ConfigSetting::OutputDir);
if(!FileUtils::HasPath(outputPath))
{ {
FileUtils::CreateDirectory(conf.configPath + conf.outputdir); FileUtils::CreateDirectory(outputPath);
FileUtils::CreateDirectory(conf.configPath + conf.outputdir + "intermediates"); std::string intermediatePath = outputPath + "intermediates";
if(!FileUtils::HasPath(intermediatePath ))
FileUtils::CreateDirectory(intermediatePath );
} }
return RunMake(filepath, flags, conf); return RunMake(filepath, flags, conf);
} }
@@ -189,6 +193,6 @@ int main(int argc, char** argv)
} }
else else
{ {
LOG_ERROR("No ", CONFIG_FILENAME, " or Makefile found."); LOG_ERROR("Couldn\'t load config file");
} }
} }
+35
View File
@@ -0,0 +1,35 @@
#include "XML.h"
#include "XMLException.h"
#include <fstream>
#include <algorithm>
XMLObject XML::FromString(const std::string& string, const std::string& filename="")
{
int startLine = 1;
int startPos = 0;
// Remove version tag.
if(string.find("<?") != std::string::npos)
{
startPos = string.find("?>") + 3;
startLine = std::count(string.begin(), string.begin()+startPos, '\n') + 1;
}
return XMLObject(string, startPos, startLine,filename);
}
XMLObject XML::FromFile(const std::string& filename)
{
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if(!file)
throw XMLException("Could not read file \""+filename+"\"");
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::string buffer;
buffer.reserve(size);
while (!file.eof())
{
buffer += file.get();
}
return FromString(buffer, filename);
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "XMLObject.h"
struct XML
{
static XMLObject FromString(const std::string& string, const std::string& filename);
static XMLObject FromFile(const std::string& fileName);
};
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "XMLObject.h"
#include <exception>
#include <string>
class XMLException : public std::exception
{
private:
std::string m_message;
public:
explicit XMLException(const std::string& message) : m_message("XMLException: " + message) {}
explicit XMLException(const std::string& message, const XMLObject::XMLLoadData& data)
: m_message("XMLException(" + data.file + ":" + std::to_string(data.line) + "): " + message)
{}
virtual const char* what() const throw()
{
return m_message.c_str();
}
};
+406
View File
@@ -0,0 +1,406 @@
#include "XMLObject.h"
#include "../Common.h"
#include "../Utils.h"
#include <cstring>
#include "XMLException.h"
XMLObject::XMLObject(const std::string& string)
{
int pos = 0;
int line = 1;
XMLLoadData data{pos, line, ""};
if(!ReadHead(string, data))
ReadBodyTail(string, data);
}
XMLObject::XMLObject(const std::string& string, int pos, int line, const std::string& file)
{
XMLLoadData data{pos, line, file};
if (!ReadHead(string, data))
ReadBodyTail(string, data);
}
XMLObject::XMLObject(const std::string& string, XMLLoadData& data)
{
if (!ReadHead(string, data))
ReadBodyTail(string, data);
}
XMLObject::XMLObject(const std::string& name, const std::map<std::string, std::string>& attributes, const std::string& text)
:name(name), attributes(attributes), text(text)
{
}
XMLObject::XMLObject(const std::string& name, const std::map<std::string, std::string>& attributes, const std::map<std::string,std::vector<XMLObject>>& objects)
: name(name), attributes(attributes), objects(objects)
{
}
bool XMLObject::HasAttribute(const std::string& property) const
{
return attributes.find(property) != attributes.end();
}
const std::string& XMLObject::GetAttribute(const std::string& property) const
{
auto it = attributes.find(property);
if (it == attributes.end())
throw XMLException((std::string("Attribute could not be found \"") + property + "\".").c_str());
return it->second;
}
const std::string& XMLObject::GetAttribute(const std::string& property, const std::string& defaultValue) const
{
auto it = attributes.find(property);
if (it == attributes.end())
return defaultValue;
return it->second;
}
unsigned int XMLObject::GetObjectCount() const
{
return objects.size();
}
std::vector<XMLObject>* XMLObject::GetObjectPtr(const std::string& name)
{
auto it = objects.find(name);
if(it == objects.end())
return nullptr;
return &it->second;
}
const std::map<std::string, std::vector<XMLObject>>& XMLObject::GetObjects() const
{
return objects;
}
const std::string& XMLObject::GetName() const
{
return name;
}
const std::string& XMLObject::GetText() const
{
return text;
}
void XMLObject::SetName(const std::string& name)
{
if(Utils::IsWord(name))
this->name = name;
else
LOG_ERROR("XML Head can only be made up of letters");
}
void XMLObject::SetText(const std::string& text)
{
this->text = text;
}
void XMLObject::AddAttribute(const std::string& property, const std::string& value)
{
if(Utils::IsWord(property))
attributes.emplace(property, value);
else
LOG_ERROR("XML property name can only be made up of letters");
}
void XMLObject::AddXMLObject(const XMLObject& object)
{
auto it = objects.find(object.name);
if(it == objects.end())
objects.emplace(object.name, std::vector<XMLObject>{object});
else
it->second.push_back(object);
}
bool XMLObject::RemoveXMLObject(const XMLObject& object)
{
auto it = objects.find(object.name);
if(it == objects.end())
return false;
bool removed = false;
for(auto it2 = it->second.begin(); it2 != it->second.end();)
{
if(*it2 == object)
{
it2 = it->second.erase(it2);
removed = true;
}
else
++it2;
}
return removed;
}
XMLObject XMLObject::GetStrippedXMLObject() const
{
if(text == "")
return XMLObject(name, attributes, objects);
else
return XMLObject(name, attributes, text);
}
////////////////////////////////////////////////////////////
// //
// Everything below here handles the reading of xml files //
// //
////////////////////////////////////////////////////////////
bool XMLObject::ReadHead(const std::string& string, XMLLoadData& data)
{
// Check if the first character is the start of and xml tag.
ReadWhiteSpace(string, data);
if (string[data.pos] != '<')
throw XMLException("Not an XML Object.", data);
// Check if there is a closing tag
size_t closing = string.find('>');
if (closing == std::string::npos)
throw XMLException("No enclosing > for opening tag.", data);
// Read the name of the tag
ReadName(string, data);
// Read all attributes of the xml tag
ReadAttributes(string, data);
// Read opening tag
if (string[data.pos] == '/')
{
data.pos++;
ReadWhiteSpace(string, data);
if (string[data.pos] != '>')
throw XMLException((std::string("Invalid character proceeding / in opening XML Tag \"") + string[data.pos] + "\".").c_str(), data);
data.pos++;
// nothing more to read.
return true;
}
ReadWhiteSpace(string, data);
if (string[data.pos] != '>')
throw XMLException((std::string("Invalid character proceeding attributes in opening XML Tag \"") + string[data.pos] + "\".").c_str(), data);
(data.pos)++;
return false;
}
void XMLObject::ReadName(const std::string& string, XMLLoadData& data)
{
data.pos++;
ReadWhiteSpace(string, data);
if (!Utils::IsLetter(string[data.pos]))
throw XMLException("Invalid XML name. Can only contain letters.", data);
name = Utils::GetWord(string, data.pos);
data.pos += name.length();
ReadWhiteSpace(string, data);
if (string[data.pos] != '/' && string[data.pos] != '>' && Utils::IsWhiteSpace(string[data.pos]))
{
throw XMLException((std::string("Invalid character proceeding name in XML Tag \"") + string[data.pos] + "\".").c_str(), data);
}
}
void XMLObject::ReadAttributes(const std::string& string, XMLLoadData& data)
{
ReadWhiteSpace(string, data);
while (string[data.pos] != '>' && string[data.pos] != '/')
{
ReadAttribute(string, data);
}
}
void XMLObject::ReadAttribute(const std::string& string, XMLLoadData& data)
{
// Read property name
std::string property = ReadXMLName(string, data);
if (property.length() == 0)
throw XMLException((std::string("Invalid character proceeding name \"") + string[data.pos] + "\".").c_str(), data);
if (attributes.count(property) > 0)
throw XMLException((std::string("Duplicate property in XML tag \"") + property + "\".").c_str(), data);
data.pos += property.length();
ReadWhiteSpace(string, data);
// Read =
if (string[data.pos] != '=')
throw XMLException((std::string("Invalid character proceeding property name in XML Tag \"") + string[data.pos] + "\".").c_str(), data);
(data.pos)++;
ReadWhiteSpace(string, data);
// Read value
if (string[data.pos] != '\"')
throw XMLException("XML property value is not inside enclosing quotes.", data);
(data.pos)++;
int valueStart = data.pos;
while (string[data.pos] != '\"') (data.pos)++;
std::string value = string.substr(valueStart, (data.pos) - valueStart);
ReplacePredefinedEntities(value, data);
(data.pos)++;
attributes.emplace(property, value);
ReadWhiteSpace(string, data);
}
void XMLObject::ReadBodyTail(const std::string& string, XMLLoadData& data)
{
ReadWhiteSpace(string, data);
if (string[data.pos] != '<')
{
ReadText(string, data);
ReadWhiteSpace(string, data);
std::string closeTag = GetClosingTag(string, data);
if (closeTag.length() == 0)
throw XMLException("Tag after XML Test was not a closing tag. XMLObject doesn't support text and other XMLObjects at the same time.", data);
return;
}
// Check if we can read the closing tag.
std::string closeTag = GetClosingTag(string, data);
while (closeTag.length() == 0)
{
AddXMLObject(XMLObject(string, data));
ReadWhiteSpace(string, data);
closeTag = GetClosingTag(string, data);
}
}
void XMLObject::ReadText(const std::string& string, XMLLoadData& data)
{
int startPos = data.pos;
while (string[data.pos] != '<') (data.pos)++;
text = string.substr(startPos, (data.pos) - startPos);
ReplacePredefinedEntities(text, data);
}
void XMLObject::ReadWhiteSpace(const std::string& string, XMLLoadData& data)
{
while (Utils::IsWhiteSpace(string[data.pos])) {
if (string[data.pos] == '\n')
(data.line)++;
(data.pos)++;
}
}
std::string XMLObject::GetClosingTag(const std::string& string, XMLLoadData& data)
{
int startPos = data.pos;
int startLine = data.line;
if (string[(data.pos)++] != '<')
{
data.pos = startPos;
data.line = startLine;
return "";
}
ReadWhiteSpace(string, data);
if (string[(data.pos)++] != '/')
{
data.pos = startPos;
data.line = startLine;
return "";
}
ReadWhiteSpace(string, data);
std::string tag = Utils::GetWord(string, data.pos);
if (tag != name)
throw XMLException((std::string("Closing tag doesn't match opening tag. (\"") + name + "\" != \"" + tag+ "\")").c_str(), data);
data.pos += tag.length();
ReadWhiteSpace(string, data);
if (string[data.pos] != '>')
throw XMLException((std::string("Invalid character in closing tag \"") + string[data.pos] + "\".").c_str(), data);
(data.pos)++;
return string.substr(startPos, (data.pos) - startPos);
}
void XMLObject::ReplacePredefinedEntities(std::string& string, XMLLoadData& data)
{
std::vector<std::pair<std::string, std::string>> entities
{
{"&quot;","\""},
{"&apos;", "\'"},
{"&lt;", "<"},
{"&gt;",">"},
{"&amp;", "&"}
};
size_t pos = string.find('&');
while(pos != std::string::npos)
{
bool found = false;
for(auto entity : entities)
{
if(strncmp(&string[pos], entity.first.c_str(), entity.first.length()) == 0)
{
string.replace(pos, entity.first.length(), entity.second);
found = true;
}
}
if(!found)
LOG_ERROR("(" + data.file + ":" + std::to_string(data.line) + "): ""Ampersand found in xml but isn't a predefined entity.");
pos = string.find('&', pos+1);
}
}
std::string XMLObject::ReadXMLName(const std::string& string, XMLLoadData& data)
{
if(!(Utils::IsLetter(string[data.pos]) ||
string[data.pos] == '_' ||
string[data.pos] == ':'))
throw XMLException(std::string("Name doesn't start with a letter."), data);
int endPos = data.pos + 1;
while (endPos < string.length() && (
Utils::IsLetter(string[endPos]) ||
string[endPos] == '_' ||
string[endPos] == '-' ||
string[endPos] == ':' ||
string[endPos] == '.'))
endPos++;
return string.substr(data.pos, endPos - data.pos);
}
std::ostream& XMLObject::WriteToStream(std::ostream& stream, int indent) const
{
for(int i = 0;i<indent;i++)
{
stream << " ";
}
stream << "<" << name;
for(auto it = attributes.begin();it!=attributes.end();++it)
{
stream << " " << it->first << "=\"" << it->second << "\"";
}
stream << ">";
if(text != "")
{
stream << text;
}
else
{
bool hasChild = false;
for(auto it = objects.begin(); it != objects.end();++it)
{
for(auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
{
stream << "\n";
it2->WriteToStream(stream, indent+1);
hasChild = true;
}
}
if(hasChild)
{
stream << "\n";
for(int i = 0;i<indent;i++)
{
stream << " ";
}
}
}
stream << "</" << name << ">";
return stream;
}
+110
View File
@@ -0,0 +1,110 @@
#pragma once
#include <string>
#include <map>
#include <set>
#include <vector>
class XMLObject
{
public:
friend class XMLexception;
struct XMLLoadData
{
int pos;
int line;
const std::string& file;
};
private:
std::string name;
std::string text;
std::map<std::string, std::string> attributes;
std::map<std::string, std::vector<XMLObject>> objects;
public:
XMLObject() {}
XMLObject(const std::string& string);
XMLObject(const std::string& string, int pos, int line, const std::string& file);
XMLObject(const std::string& string, XMLLoadData& data);
XMLObject(const std::string& name, const std::map<std::string, std::string>& properties, const std::string& text);
XMLObject(const std::string& name, const std::map<std::string, std::string>& properties, const std::map<std::string, std::vector<XMLObject>>& objects);
bool HasAttribute(const std::string& property) const;
const std::string& GetAttribute(const std::string& property) const;
const std::string& GetAttribute(const std::string& property, const std::string& defaultValue) const;
unsigned int GetObjectCount() const;
std::vector<XMLObject>* GetObjectPtr(const std::string& name);
const std::map<std::string, std::vector<XMLObject>>& GetObjects() const;
const std::string& GetName() const;
const std::string& GetText() const;
XMLObject GetStrippedXMLObject() const;
void SetName(const std::string& name);
void SetText(const std::string& text);
void AddAttribute(const std::string& property, const std::string& value);
void AddXMLObject(const XMLObject& object);
bool RemoveXMLObject(const XMLObject& object);
friend bool operator<(const XMLObject& obj1, const XMLObject& obj2)
{
return obj1.name < obj2.name;
}
std::ostream& WriteToStream(std::ostream& stream, int indent = 0) const;
friend std::ostream& operator<<(std::ostream& stream, const XMLObject& object)
{
return object.WriteToStream(stream);
}
friend bool operator==(const XMLObject& object1, const XMLObject& object2)
{
if(object1.attributes.size() != object2.attributes.size())
return false;
if(object1.objects.size() != object2.objects.size())
return false;
if(object1.name != object2.name)
return false;
if(object1.GetText() != object2.GetText())
return false;
{
auto it1 = object1.attributes.begin();
auto it2 = object2.attributes.begin();
while(it1 != object1.attributes.end())
{
if(it1->first != it2->first || it1->second != it1->second)
return false;
++it1;
++it2;
}
}
{
auto it1 = object1.objects.begin();
auto it2 = object2.objects.begin();
while(it1 != object1.objects.end())
{
if(it1->second != it2->second)
return false;
++it1;
++it2;
}
}
return true;
}
private:
std::string GetClosingTag(const std::string& string, XMLLoadData& data);
// Returns true if the head contained closing tag.
bool ReadHead(const std::string& string, XMLLoadData& data);
void ReadName(const std::string& string, XMLLoadData& data);
void ReadAttribute(const std::string& string, XMLLoadData& data);
void ReadAttributes(const std::string& string, XMLLoadData& data);
void ReadBodyTail(const std::string& string, XMLLoadData& data);
void ReadText(const std::string& string, XMLLoadData& data);
void ReadWhiteSpace(const std::string& string, XMLLoadData& data);
void ReplacePredefinedEntities(std::string& string, XMLLoadData& data);
std::string ReadXMLName(const std::string& string, XMLLoadData& data);
};