Rework how the config file is read

Still need to remove reduntant code and test it much more thoroughly.
This commit is contained in:
Thraix
2019-10-18 11:44:40 +02:00
parent f3ab790912
commit d9f7dc4d17
21 changed files with 934 additions and 387 deletions
+9 -9
View File
@@ -1,4 +1,4 @@
# This Makefile was generated using MakeGen v1.2.0 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++
@@ -8,7 +8,7 @@ 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)/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=
LDFLAGS=
LIBS=$(LIBDIR)
@@ -33,28 +33,28 @@ $(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 src/xml/XMLObject.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 -[10%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/ConfigFile.o : src/ConfigFile.cpp src/ConfigFile.h src/xml/XMLObject.h src/FileUtils.h src/Common.h src/Utils.h src/compatibility/ConfigFileConf.h src/xml/XML.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 -[20%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/HFileGen.o : src/HFileGen.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h src/xml/XMLObject.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 -[30%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/IncludeDeps.o : src/IncludeDeps.cpp src/Common.h src/IncludeDeps.h src/ConfigFile.h src/xml/XMLObject.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 -[40%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/Makefile.o : src/Makefile.cpp src/IncludeDeps.h src/ConfigFile.h src/xml/XMLObject.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 -[50%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(OBJPATH)/Utils.o : src/Utils.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h src/xml/XMLObject.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 -[60%]- $<)
$(CC) $(CFLAGS) -o $@ $<
$(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/xml/XMLObject.h src/FileUtils.h src/Utils.h src/HFileGen.h src/Makefile.h src/Timer.h
$(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
+12 -4
View File
@@ -1,14 +1,22 @@
<makegen>
<configuration name="Release">
<define>_DEBUG</define>
<generatehfile>false</generatehfile>
<hfile>MakeGen.h</hfile>
<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>
<target>Release</target>
<version>v1.3.0</version>
</makegen>
</makegen>
+18 -3
View File
@@ -28,9 +28,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)
@@ -44,3 +44,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);
+356 -162
View File
@@ -7,33 +7,321 @@
#include <algorithm>
#include <fstream>
ConfigFile::ConfigFile()
: outputdir("bin/"), srcdir("src/"), outputname(""), projectname(FileUtils::GetCurrentDirectory()), hFile(projectname+".h"), 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
std::remove_if(
outputdir.begin(),
outputdir.end(),
[](unsigned char c)
{
return (c < 'a' || c > 'z') && c != '_';
});
// 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,38 +343,35 @@ std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath,
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 + "makegen.conf");
oldFile = true;
f = std::ifstream(filepath + CONFIG_FILENAME);
}
// Check if the file exists
if(f.good())
{
f.close();
std::optional<ConfigFile> conf;
if(oldFile)
conf = ConfigFileConf::Load(realPath);
else
conf = ConfigFile::Load(realPath);
if(!conf)
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.
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;
conf.dependencyConfigs.push_back(*dep);
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;
}
}
@@ -95,103 +380,6 @@ std::optional<ConfigFile> ConfigFile::GetConfigFile(const std::string& filepath,
return {};
}
std::optional<ConfigFile> ConfigFile::Load(const std::string& filedir)
{
XMLObject object{XML::FromFile(filedir + CONFIG_FILENAME)};
const std::string& target = object.GetObject("target", {XMLObject{"target", {}, "Release"}})[0].GetText();
const std::vector<XMLObject>& configurations = object.GetObject("configuration");
const XMLObject* configuration = nullptr;
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)
{
configuration = &(*it);
break;
}
}
if(configuration == nullptr)
{
LOG_ERROR("No configuration matching target: ", target);
return {};
}
ConfigFile conf;
conf.configPath = filedir;
conf.projectname = configuration->GetObject("projectname",
{XMLObject{"projectname", {}, conf.projectname}})[0].GetText();
conf.outputname = configuration->GetObject("outputname",
{XMLObject{"outputname", {}, conf.outputname}})[0].GetText();
conf.srcdir = configuration->GetObject("srcdir",
{XMLObject{"srcdir", {}, conf.srcdir}})[0].GetText();
conf.outputdir = configuration->GetObject("outputdir",
{XMLObject{"outputdir", {}, conf.outputdir}})[0].GetText();
conf.hFile = configuration->GetObject("hfile",
{XMLObject{"hfile", {}, conf.hFile}})[0].GetText();
std::string outputtype = configuration->GetObject("outputtype",
{XMLObject{"outputtype", {}, "executable"}})[0].GetText();
if(conf.srcdir[conf.srcdir.size()-1] != '/')
conf.srcdir += '/';
if(conf.outputdir[conf.srcdir.size()-1] != '/')
conf.outputdir += '/';
if(outputtype == "executable")
conf.executable = true;
else if(outputtype == "staticlibrary")
{
conf.executable = false;
conf.shared = false;
}
else if(outputtype == "sharedlibrary")
{
conf.executable = false;
conf.shared = true;
}
else
{
LOG_ERROR("Invalid outputtype: ", outputtype);
LOG_ERROR("Valid arguments are executable, staticlibrary and sharedlibrary");
}
conf.generateHFile = configuration->GetObject("generatehfile",
{XMLObject{"generatehfile", {}, conf.generateHFile ? "true" : "false"}})[0].GetText() == "true";
const int vectorCount = 6;
std::tuple<std::vector<XMLObject>, std::vector<std::string>*, bool> vectors[vectorCount] = {
{configuration->GetObject("library"), &conf.libs, false},
{configuration->GetObject("libdir"), &conf.libdirs, true},
{configuration->GetObject("includedir"), &conf.includedirs, true},
{configuration->GetObject("define"), &conf.defines, false},
{configuration->GetObject("compileflag"), &conf.flags, false},
{configuration->GetObject("dependency"), &conf.dependencies, true}
};
for(int i = 0;i<vectorCount;++i)
{
const std::vector<XMLObject>& xmls = std::get<0>(vectors[i]);
std::vector<std::string>* vec = std::get<1>(vectors[i]);
bool isDir = std::get<2>(vectors[i]);
for(auto it = xmls.begin(); it != xmls.end();++it)
{
if(it->GetText() != "")
{
std::string s = it->GetText();
if(isDir && s[s.size()-1] != '/')
s += '/';
vec->push_back(s);
}
}
}
return conf;
}
void ConfigFile::InputBoolean(const std::string& inputText, bool& b)
{
std::string input;
@@ -238,38 +426,39 @@ 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);
void ConfigFile::Save() const
{
// 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
@@ -281,7 +470,7 @@ void ConfigFile::Save() const
configuration.AddXMLObject(XMLObject("outputname", {}, outputname));
configuration.AddXMLObject(XMLObject("srcdir", {}, srcdir));
configuration.AddXMLObject(XMLObject("outputdir", {}, outputdir));
configuration.AddXMLObject(XMLObject("hfile", {}, hFile));
configuration.AddXMLObject(XMLObject("hfilename", {}, hFile));
configuration.AddXMLObject(XMLObject("outputtype", {},
executable ? "executable" : (shared ? "sharedlibrary" : "staticlibrary")));
configuration.AddXMLObject(XMLObject("generatehfile", {}, generateHFile ? "true" : "false"));
@@ -289,17 +478,22 @@ void ConfigFile::Save() const
for(auto it = libs.begin();it != libs.end(); ++it)
configuration.AddXMLObject({"library",{},*it});
for(auto it = libdirs.begin();it != libdirs.end(); ++it)
configuration.AddXMLObject({"libdir",{},*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 = flags.begin();it != flags.end(); ++it)
configuration.AddXMLObject({"compileflag",{},*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);
std::ofstream file("makegen.xml");
file << makegen;
return ConfigFile{makegen, FileUtils::GetRealPath("./")};
}
void ConfigFile::Save() const
{
std::ofstream file("makegen.xml");
file << config;
}
+33 -19
View File
@@ -1,5 +1,6 @@
#pragma once
#include "ConfigUtils.h"
#include "xml/XMLObject.h"
#include <map>
@@ -11,29 +12,42 @@ static const std::string CONFIG_FILENAME = "makegen.xml";
class ConfigFile
{
public:
private:
ConfigCache cache;
XMLObject config;
// Current configuration
std::string target;
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;
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:
+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);
};
+14 -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,7 +93,7 @@ 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)});
}
}
}
+5 -5
View File
@@ -1,7 +1,5 @@
#pragma once
#include "ConfigFile.h"
#include <set>
#include <string>
@@ -24,12 +22,14 @@ 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);
+40 -10
View File
@@ -42,9 +42,9 @@ ConfigFileConf::ConfigFileConf()
outputname += ".out";
}
ConfigFile ConfigFileConf::Load(const std::string& filepath)
void ConfigFileConf::CreateXMLFile(const std::string& filepath)
{
ConfigFile conf;
ConfigFileConf conf;
conf.configPath = filepath;
unsigned int loadFlag = 0;
@@ -152,13 +152,43 @@ ConfigFile ConfigFileConf::Load(const std::string& filepath)
}
}
}
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("makegen.xml");
xmlFile << makegen;
}
if(conf.hFile == "")
conf.hFile = conf.projectname+".h";
LOG_INFO("------ COULDN\'T FIND makegen.xml. BUT FOUND OLD makegen.conf.");
LOG_INFO("------ GENERATING NEW CONFIGURATION FILE");
conf.Save();
return conf;
}
+1 -1
View File
@@ -29,6 +29,6 @@ class ConfigFileConf
public:
ConfigFileConf();
static ConfigFile Load(const std::string& filename);
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");
}
}
+25 -4
View File
@@ -66,13 +66,13 @@ unsigned int XMLObject::GetObjectCount() const
return objects.size();
}
const std::vector<XMLObject>& XMLObject::GetObject(const std::string& name, const std::vector<XMLObject>& defaults) const
std::vector<XMLObject>* XMLObject::GetObjectPtr(const std::string& name)
{
auto it = objects.find(name);
if(it == objects.end())
return defaults;
return nullptr;
return it->second;
return &it->second;
}
const std::map<std::string, std::vector<XMLObject>>& XMLObject::GetObjects() const
@@ -110,6 +110,7 @@ void XMLObject::AddAttribute(const std::string& property, const std::string& val
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);
@@ -119,6 +120,26 @@ void XMLObject::AddXMLObject(const XMLObject& object)
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 == "")
@@ -374,7 +395,7 @@ std::ostream& XMLObject::WriteToStream(std::ostream& stream, int indent) const
stream << "\n";
for(int i = 0;i<indent;i++)
{
stream << "\t";
stream << " ";
}
}
}
+39 -1
View File
@@ -35,7 +35,7 @@ class XMLObject
const std::string& GetAttribute(const std::string& property, const std::string& defaultValue) const;
unsigned int GetObjectCount() const;
const std::vector<XMLObject>& GetObject(const std::string& name, const std::vector<XMLObject>& defaults = {}) 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;
@@ -45,6 +45,7 @@ class XMLObject
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)
{
@@ -57,6 +58,43 @@ class XMLObject
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.