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
# https://github.com/Thraix/MakeGen
CC=@g++
@@ -7,8 +7,8 @@ MKDIR_P=mkdir -p
BIN=bin/
OBJPATH=$(BIN)intermediates
INCLUDES=
OBJECTS=$(OBJPATH)/ConfigCLI.o $(OBJPATH)/ConfigFile.o $(OBJPATH)/HFileGen.o $(OBJPATH)/IncludeDeps.o $(OBJPATH)/Makefile.o $(OBJPATH)/Utils.o $(OBJPATH)/main.o
CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3 -D_DEBUG
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
LIBDIR=
LDFLAGS=
LIBS=$(LIBDIR)
@@ -33,24 +33,33 @@ $(OUTPUT): $(OBJECTS)
install: all
$(info Installing MakeGen to /usr/bin/)
@cp $(OUTPUT) /usr/bin/makegen
$(OBJPATH)/ConfigCLI.o : src/ConfigCLI.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h
$(info -[14%]- $<)
$(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 -[10%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/ConfigFile.o : src/ConfigFile.cpp src/ConfigFile.h src/FileUtils.h src/Common.h src/Utils.h
$(info -[28%]- $<)
$(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 -[20%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/HFileGen.o : src/HFileGen.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h src/HFileGen.h
$(info -[42%]- $<)
$(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 -[30%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/IncludeDeps.o : src/IncludeDeps.cpp src/Common.h src/IncludeDeps.h src/ConfigFile.h src/FileUtils.h src/Utils.h
$(info -[57%]- $<)
$(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 -[40%]- $<)
$(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
$(info -[71%]- $<)
$(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 -[50%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/Utils.o : src/Utils.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h
$(info -[85%]- $<)
$(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 -[60%]- $<)
$(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%]- $<)
$(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
#define MAKEGEN_VERSION_MAJOR 1
// Release, should be backwards compatible
#define MAKEGEN_VERSION_RELEASE 2
// Release, should be backwards compatible with any minor version
#define MAKEGEN_VERSION_RELEASE 3
// 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))
@@ -29,9 +29,9 @@ const static unsigned int FLAG_SIMPLE = BIT(9);
const static unsigned int FLAG_CONFIG = BIT(10);
#define LOG_INFO(...) Log(__VA_ARGS__); std::cout << std::endl
#define LOG_WARNING(...) Log(__VA_ARGS__); std::cout << std::endl
#define LOG_ERROR(...) Log(__VA_ARGS__); std::cout << std::endl
#define LOG_INFO(...) LogHelper(__VA_ARGS__)
#define LOG_WARNING(...) LogHelper(__VA_ARGS__)
#define LOG_ERROR(...) LogHelper(__VA_ARGS__)
template <typename T>
void Log(const T& var)
@@ -45,3 +45,18 @@ void Log(const T& var, const Ts& ...vars)
Log(var);
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()
{
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]
@@ -49,7 +49,7 @@ Usage: makegen conf add <setting> <value> [<values>]
Valid settings are:
library Library
libdir Library directory
librarydir Library directory
includedir Include directory
define Preprocessor define
cflag g++ compiler flags
@@ -65,7 +65,7 @@ Usage: makegen conf remove <setting> <value> [<
Valid settings are
library Library name
libdir Library directory
librarydir Library directory
includedir Include directory
define Preprocessor define
cflag g++ compiler flags
@@ -81,13 +81,13 @@ Usage: makegen conf set <setting> <value>
Valid string settings are:
outputdir Directory of the compiled output
output Name of the output executable/library
name Name of the project
outputname Name of the output executable/library
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
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
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:
library Library name
libdir Library directory
librarydir Library directory
includedir Include directory
define Preprocessor define
cflag g++ compiler flags
dependency Project which current project depends on
outputdir Directory of the compiled output
output Name of the output executable/library
name Name of the project
outputname Name of the output executable/library
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
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)");
}
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}
};
}
std::map<std::string, std::string*> ConfigCLI::GetSettingStringMap(ConfigFile& config)
ConfigSetting ConfigCLI::CLIStringToSetting(const std::string& s)
{
return {
{"outputdir", &config.outputdir},
{"output", &config.outputname},
{"name", &config.projectname},
{"hfile", &config.hFile},
};
}
std::map<std::string, bool*> ConfigCLI::GetSettingBoolMap(ConfigFile& config)
{
return {
{"executable", &config.executable},
{"shared", &config.shared},
{"genhfile", &config.generateHFile}
static std::map<std::string, ConfigSetting> map{
{"srcdir", ConfigSetting::SourceDir},
{"outputdir", ConfigSetting::OutputDir},
{"outputname", ConfigSetting::OutputName},
{"outputtype", ConfigSetting::OutputType},
{"projectname", ConfigSetting::ProjectName},
{"hfile", ConfigSetting::HFileName},
{"library", ConfigSetting::Library},
{"librarydir", ConfigSetting::LibraryDir},
{"includedir", ConfigSetting::IncludeDir},
{"define", ConfigSetting::Define},
{"cflag", ConfigSetting::CFlag},
{"dependency", ConfigSetting::Dependency},
{"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)
@@ -168,7 +160,7 @@ int ConfigCLI::Gen(int argc, char** argv)
}
if(option == "default")
{
ConfigFile{}.Save();
ConfigFile{FileUtils::GetRealPath("."),0}.Save();
return 0;
}
else
@@ -191,25 +183,24 @@ int ConfigCLI::Add(int argc, char** argv, ConfigFile& config)
return 1;
}
auto settingMap = GetSettingVectorMap(config);
auto it = settingMap.find(argv[1]);
if(it == settingMap.end())
ConfigSetting setting = CLIStringToSetting(argv[1]);
if(!ConfigUtils::IsVectorSetting(setting))
{
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;
}
std::vector<std::string>* setting = it->second;
std::set<std::string> settingSet{setting->begin(), setting->end()};
for(int i = 2; i<argc;++i)
{
auto res = settingSet.emplace(argv[i]);
if(!res.second)
{
LOG_ERROR("Duplicate value: ", argv[i]);
}
config.AddSettingVectorString(setting, argv[i]);
}
*setting = {settingSet.begin(), settingSet.end()};
config.Save();
return 0;
@@ -228,27 +219,25 @@ int ConfigCLI::Remove(int argc, char** argv, ConfigFile& config)
return 1;
}
auto settingMap = GetSettingVectorMap(config);
auto it = settingMap.find(argv[1]);
if(it == settingMap.end())
ConfigSetting setting = CLIStringToSetting(argv[1]);
if(!ConfigUtils::IsVectorSetting(setting))
{
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;
}
std::vector<std::string>* setting = it->second;
std::set<std::string> settingSet{setting->begin(), setting->end()};
for(int i = 2; i<argc;++i)
{
auto it = settingSet.find(argv[i]);
if(it == settingSet.end())
{
LOG_ERROR("No such value in setting: ", argv[i]);
}
else
settingSet.erase(it);
config.RemoveSettingVectorString(setting, argv[i]);
}
*setting = {settingSet.begin(), settingSet.end()};
config.Save();
return 0;
@@ -267,31 +256,22 @@ int ConfigCLI::Set(int argc, char** argv, ConfigFile& config)
return 1;
}
auto settingStringMap = GetSettingStringMap(config);
auto it1 = settingStringMap.find(argv[1]);
if(it1 == settingStringMap.end())
ConfigSetting setting = CLIStringToSetting(argv[1]);
if(!ConfigUtils::IsStringSetting(setting) && !ConfigUtils::IsBoolSetting(setting))
{
auto settingBoolMap = GetSettingBoolMap(config);
auto it2 = settingBoolMap.find(argv[1]);
if(it2 == settingBoolMap.end())
if(setting == ConfigSetting::Invalid)
{
LOG_ERROR("Invalid setting: ", argv[1]);
return 1;
LOG_ERROR("No such setting: ", argv[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
{
LOG_ERROR("Invalid boolean value: ", argv[2]);
return 1;
LOG_ERROR("Cannot set setting which supports multiple arguments");
LOG_ERROR("use add or remove instead.");
}
config.Save();
return 0;
return 1;
}
*it1->second = argv[2];
config.SetSettingString(setting, argv[2]);
config.Save();
return 0;
}
@@ -308,29 +288,9 @@ int ConfigCLI::Get(int argc, char** argv, ConfigFile& config)
LOG_ERROR("get needs exactly one parameter");
return 1;
}
auto settingVectorMap = GetSettingVectorMap(config);
auto itV = settingVectorMap.find(argv[1]);
if(itV == settingVectorMap.end())
{
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)
ConfigSetting setting = CLIStringToSetting(argv[1]);
std::vector<std::string> vector = config.GetSetting(setting);
for(auto it = vector.begin(); it != vector.end(); ++it)
{
LOG_INFO(*it);
}
@@ -351,7 +311,7 @@ int ConfigCLI::Main(int argc, char** argv)
{
if(config)
{
LOG_ERROR("Config file already exist (makegen.conf)");
LOG_ERROR("Config file already exist (", CONFIG_FILENAME, ")");
return 1;
}
return Gen(argc-1, &argv[1]);
+1 -3
View File
@@ -14,9 +14,7 @@ struct ConfigCLI
static void DisplaySetHelp();
static void DisplayGetHelp();
static std::map<std::string, std::vector<std::string>*> GetSettingVectorMap(ConfigFile& config);
static std::map<std::string, std::string*> GetSettingStringMap(ConfigFile& config);
static std::map<std::string, bool*> GetSettingBoolMap(ConfigFile& config);
static ConfigSetting CLIStringToSetting(const std::string& s);
static int Gen(int argc, char** argv);
static int Add(int argc, char** argv, ConfigFile& config);
+387 -225
View File
@@ -1,43 +1,327 @@
#include "ConfigFile.h"
#include "FileUtils.h"
#include "compatibility/ConfigFileConf.h"
#include "xml/XML.h"
#include <algorithm>
#include <fstream>
#define FLAG_NONE 0
#define FLAG_VECTOR 1
#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)
ConfigFile::ConfigFile(const std::string& path, int)
: configPath{path}
{
// 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);
});
// Create xml
XMLObject makegen("makegen", {}, std::map<std::string, std::vector<XMLObject>>{});
// Removes all other characters
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());
// 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"));
// Add suffix
outputname += ".out";
XMLObject configuration("configuration", {{"name", "Release"}}, std::map<std::string, std::vector<XMLObject>>{});
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)
@@ -55,27 +339,39 @@ std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath,
return {};
}
bool oldFile = false;
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
if(f.good())
{
f.close();
ConfigFile conf = ConfigFile::Load(realPath);
ConfigFile conf = ConfigFile(filepath);
if(conf.hasInitError)
return {};
loadedConfigs.emplace(realPath, conf);
std::vector<std::string>& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency);
// 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)
{
conf.dependencyConfigs.push_back(*dep);
conf.dependencies[i] = dep->configPath;
dependencies[i] = dep->configPath;
}
else
{
// Remove the dependency since it is already accounted for
conf.dependencies.erase(conf.dependencies.begin() + i);
dependencies.erase(dependencies.begin() + i);
--i;
}
}
@@ -84,124 +380,6 @@ std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath,
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)
{
std::string input;
@@ -248,90 +426,74 @@ void ConfigFile::InputMultiple(const std::string& inputText, std::vector<std::st
ConfigFile ConfigFile::Gen()
{
ConfigFile conf;
InputBoolean("Should it be compiled as an executable? (y/n)", conf.executable);
bool executable, shared, generateHFile;
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(conf.executable)
if(executable)
{
InputMultiple("Enter library:", conf.libs,false);
InputMultiple("Enter library directory:", conf.libdirs,true);
InputMultiple("Enter project dependencies:", conf.dependencies,true);
InputMultiple("Enter library:", libs,false);
InputMultiple("Enter library directory:", libdirs,true);
InputMultiple("Enter project dependencies:", dependencies,true);
}
else
{
InputBoolean("Should it be compiled as a shared library? (y/n)", conf.shared);
InputBoolean("Should it compile a project h-file? (y/n):", conf.generateHFile);
if(conf.generateHFile)
InputBoolean("Should it be compiled as a shared library? (y/n)", shared);
InputBoolean("Should it compile a project h-file? (y/n):", 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);
InputString("Enter source directories:", conf.srcdir, true, false);
InputMultiple("Enter preprocessor definitions:", conf.defines, false);
InputMultiple("Enter compile flags:", conf.flags, false);
InputString("Enter output directory (default: bin):", conf.outputdir, true, true);
if(conf.outputdir == "")
conf.outputdir = "bin/";
InputString("Enter a name for the project:", conf.projectname, false, false);
InputString("Enter a name for the output file:", conf.outputname, false, false);
return conf;
InputMultiple("Enter include directory:", includedirs, true);
InputString("Enter source directories:", srcdir, true, false);
InputMultiple("Enter preprocessor definitions:", defines, false);
InputMultiple("Enter compile flags:", compileFlags, false);
InputString("Enter output directory (default: bin):", outputdir, true, true);
if(outputdir == "")
outputdir = "bin/";
InputString("Enter a name for the project:", projectname, false, false);
InputString("Enter a name for the output file:", outputname, false, false);
// 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
{
std::ofstream file("makegen.conf");
file << "#libs" << std::endl;
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();
std::ofstream file("makegen.xml");
file << config;
}
+37 -19
View File
@@ -1,40 +1,58 @@
#pragma once
#include "ConfigUtils.h"
#include "xml/XMLObject.h"
#include <map>
#include <optional>
#include <string>
#include <vector>
static const std::string CONFIG_FILENAME = "makegen.conf";
static const std::string CONFIG_FILENAME = "makegen.xml";
class ConfigFile
{
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;
private:
ConfigCache cache;
std::string outputdir;
std::string srcdir;
std::string outputname;
std::string projectname;
std::string hFile;
bool executable;
bool shared;
bool generateHFile;
XMLObject config;
// Current configuration
std::string target;
std::string configPath;
std::vector<ConfigFile> dependencyConfigs;
bool hasInitError = false;
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;
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 std::optional<ConfigFile> GetConfigFile(const std::string& filepath = "./");
private:
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 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);
+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
getcwd(path, sizeof(path));
std::string dir = path;
size_t pos = dir.find_last_of("/");
return GetTopDirectory(path);
}
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)
{
LOG_ERROR("Couldn't find / (slash) in directory. This shouldn't occur.");
assert(false);
}
return dir.substr(pos+1);
return dir.substr(pos + 1, dirEnd - pos);
}
static std::string GetRealPath(const std::string& filename)
+4 -4
View File
@@ -3,11 +3,11 @@
#include "FileUtils.h"
#include <set>
void HFileGen::Create(const ConfigFile& conf)
void HFileGen::Create(ConfigFile& conf)
{
std::set<std::string> hFiles;
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);
// include paramenter with the path of the file
// 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)
{
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
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;
for(auto&& hFile : hFiles)
os << "#include <" << hFile << ">" << std::endl;
+1 -1
View File
@@ -5,5 +5,5 @@
class HFileGen
{
public:
static void Create(const ConfigFile& conf);
static void Create(ConfigFile& conf);
};
+1 -1
View File
@@ -32,7 +32,7 @@ class IncludeDeps
printCounter++;
printSet.emplace(filepath);
if(!projectHFile)
stream << FileUtils::GetRelativePath(conf.configPath, filepath);
stream << FileUtils::GetRelativePath(conf.GetConfigPath(), filepath);
for(auto it = dependencies.begin();it!=dependencies.end();++it)
{
stream << " ";
+32 -24
View File
@@ -6,7 +6,7 @@
#include <fstream>
#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<std::string> cppFiles;
@@ -15,14 +15,15 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
else
Utils::GetCppAndHFiles(conf, hFiles, cppFiles);
std::ofstream outputFile(conf.configPath + "Makefile");
outputFile << "# This Makefile was generated using MakeGen "<< MAKEGEN_VERSION<< " made by Tim Håkansson" << std::endl;
std::ofstream outputFile(conf.GetConfigPath()+ "Makefile");
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 << "# https://github.com/Thraix/MakeGen" << 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;
else
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 << "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 << "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 << " ";
}
@@ -47,7 +49,7 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
outputFile << "$(OBJPATH)/" << it->substr(slash, extensionPos - slash) << ".o ";
}
outputFile << std::endl;
if(conf.executable || !conf.shared)
if(outputtype == "executable" || outputtype != "sharedlibrary")
{
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 ";
}
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 << " ";
}
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 << std::endl;
if(conf.executable)
if(outputtype == "executable")
{
std::vector<std::string>& libdirs= conf.GetSettingVectorString(ConfigSetting::LibraryDir);
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 << std::endl;
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 << std::endl;
std::vector<std::string>& libs = conf.GetSettingVectorString(ConfigSetting::Library);
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 << std::endl;
if(!conf.dependencies.empty())
std::vector<std::string>& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency);
if(!dependencies.empty())
{
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 << 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;
// All
@@ -114,7 +121,7 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
// Run
outputFile << "run: all" << std::endl;
if(conf.executable)
if(outputtype == "executable")
{
outputFile << "\t@./$(OUTPUT)" << std::endl;
}
@@ -130,25 +137,26 @@ void Makefile::Save(const ConfigFile& conf, unsigned int flags)
// Output file
outputFile << "$(OUTPUT): $(OBJECTS)" << 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;
else
outputFile << "\t$(CO) $(OUTPUT) $(OBJECTS)" << std::endl;
// Install
outputFile << "install: all" << std::endl;
outputFile << "\t$(info Installing " << conf.projectname <<" to /usr/bin/)" << std::endl;
outputFile << "\t@cp $(OUTPUT) /usr/bin/" << conf.outputname << std::endl;
outputFile << "\t$(info Installing " << conf.GetSettingString(ConfigSetting::ProjectName) <<" to /usr/bin/)" << std::endl;
outputFile << "\t@cp $(OUTPUT) /usr/bin/" << conf.GetSettingString(ConfigSetting::OutputName) << std::endl;
std::map<std::string, IncludeDeps*> dependencies;
size_t i = 0;
for(auto it = cppFiles.begin(); it!= cppFiles.end();++it)
for(auto it = cppFiles.begin(); it != cppFiles.end();++it)
{
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())
{
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 slash = it->find_last_of("/")+1;
std::string oFile = it->substr(slash, extensionPos - slash)+".o ";
+1 -1
View File
@@ -5,5 +5,5 @@
class Makefile
{
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 "ConfigFile.h"
#include "FileUtils.h"
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);
}
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::string path = conf.configPath + conf.srcdir;
std::string path = conf.GetConfigPath() + conf.GetSettingString(ConfigSetting::SourceDir);
FileUtils::GetAllFiles(path, files);
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::string path = conf.configPath + conf.srcdir;
std::string path = conf.GetConfigPath() + conf.GetSettingString(ConfigSetting::SourceDir);
FileUtils::GetAllFiles(path,files);
// include paramenter with the path of the file
// 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.
// Cyclic dependencies probably shouldn't exist.
// 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::string depSrcDir = dependencyDir + conf.srcdir;
std::string depSrcDir = dependencyDir + conf.GetSettingString(ConfigSetting::SourceDir);
FileUtils::GetAllFiles(depSrcDir, files);
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")
{
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
#include "ConfigFile.h"
#include <set>
#include <string>
@@ -24,10 +22,18 @@ struct HFile
}
};
class ConfigFile;
struct Utils
{
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 GetCppAndHFiles(const 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 GetCppFiles(ConfigFile& conf, 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, 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\")");
}
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);
Makefile::Save(conf, flags);
}
@@ -115,7 +115,7 @@ unsigned int ReadFlags(int argc, char** argv)
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;
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);
}
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 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)
return success;
}
LOG_INFO("-----------------------------------");
LOG_INFO("Building ", conf.projectname);
LOG_INFO("Building ", conf.GetSettingString(ConfigSetting::ProjectName));
LOG_INFO("Generating Makefile...");
Timer timer;
GenMakefile(conf, flags);
LOG_INFO("Took ", round(timer.Elapsed()*1000.0)/1000.0,"s");
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(conf.configPath + conf.outputdir + "intermediates");
FileUtils::CreateDirectory(outputPath);
std::string intermediatePath = outputPath + "intermediates";
if(!FileUtils::HasPath(intermediatePath ))
FileUtils::CreateDirectory(intermediatePath );
}
return RunMake(filepath, flags, conf);
}
@@ -189,6 +193,6 @@ int main(int argc, char** argv)
}
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);
};