diff --git a/Makefile b/Makefile index af90f28..a1a1d21 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/makegen.xml b/makegen.xml index 9cb4cbf..c1f7e8d 100644 --- a/makegen.xml +++ b/makegen.xml @@ -1,14 +1,22 @@ - _DEBUG false - MakeGen.h + MakeGen.h + bin/ + makegen + executable + MakeGen + src/ + + + _DEBUG + false + MakeGen.h bin/ makegen executable MakeGen src/ - Release v1.3.0 - \ No newline at end of file + diff --git a/src/Common.h b/src/Common.h index 2790775..e6a774c 100755 --- a/src/Common.h +++ b/src/Common.h @@ -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 void Log(const T& var) @@ -44,3 +44,18 @@ void Log(const T& var, const Ts& ...vars) Log(var); Log(vars...); } + +template +void LogHelper(const T& var) +{ + Log(var); + std::cout << std::endl; +} + +template +void LogHelper(const T& var, const Ts& ...vars) +{ + Log(var); + Log(vars...); + std::cout << std::endl; +} diff --git a/src/ConfigCLI.cpp b/src/ConfigCLI.cpp index b188905..2114971 100644 --- a/src/ConfigCLI.cpp +++ b/src/ConfigCLI.cpp @@ -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 [] [--help] @@ -49,7 +49,7 @@ Usage: makegen conf add [] 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 [< 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 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 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*> 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 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 ConfigCLI::GetSettingBoolMap(ConfigFile& config) -{ - return { - {"executable", &config.executable}, - {"shared", &config.shared}, - {"genhfile", &config.generateHFile} + static std::map 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* setting = it->second; - std::set settingSet{setting->begin(), setting->end()}; for(int i = 2; i* setting = it->second; - std::set settingSet{setting->begin(), setting->end()}; for(int i = 2; isecond) = 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 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]); diff --git a/src/ConfigCLI.h b/src/ConfigCLI.h index 88335a1..4335ae6 100644 --- a/src/ConfigCLI.h +++ b/src/ConfigCLI.h @@ -14,9 +14,7 @@ struct ConfigCLI static void DisplaySetHelp(); static void DisplayGetHelp(); - static std::map*> GetSettingVectorMap(ConfigFile& config); - static std::map GetSettingStringMap(ConfigFile& config); - static std::map 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); diff --git a/src/ConfigFile.cpp b/src/ConfigFile.cpp index be7b7a1..2b2ae32 100755 --- a/src/ConfigFile.cpp +++ b/src/ConfigFile.cpp @@ -7,33 +7,321 @@ #include #include -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>{}); - // 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>{}); + 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* 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* 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* 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& 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* values = GetConfiguration().GetObjectPtr(sSetting); + if(values == nullptr) + return cache.vecStrings.emplace(sSetting, std::vector{}).first->second; + + std::vector 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 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* 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{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* 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* 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::GetConfigFile(const std::string& filepath) @@ -55,38 +343,35 @@ std::optional 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 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& 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 dep = GetConfigFile(conf->configPath + conf->dependencies[i], loadedConfigs); + std::optional 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::GetConfigFile(const std::string& filepath, return {}; } - -std::optional 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& 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*, 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& xmls = std::get<0>(vectors[i]); - std::vector* 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 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>{}); // 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; } diff --git a/src/ConfigFile.h b/src/ConfigFile.h index 6e8cc71..e058497 100755 --- a/src/ConfigFile.h +++ b/src/ConfigFile.h @@ -1,5 +1,6 @@ #pragma once +#include "ConfigUtils.h" #include "xml/XMLObject.h" #include @@ -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 libs; - std::vector libdirs; - std::vector includedirs; - std::vector defines; - std::vector flags; - std::vector dependencies; - - std::string outputdir; - std::string srcdir; - std::string outputname; - std::string projectname; - std::string hFile; - bool executable; - bool shared; - bool generateHFile; - std::vector 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& GetSettingVectorString(ConfigSetting setting); + std::vector 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 GetConfigFile(const std::string& filepath = "./"); private: diff --git a/src/ConfigUtils.h b/src/ConfigUtils.h new file mode 100644 index 0000000..bfcf066 --- /dev/null +++ b/src/ConfigUtils.h @@ -0,0 +1,242 @@ +#pragma once + +#include "Common.h" +#include "FileUtils.h" + +#include +#include +#include +#include + +struct ConfigCache +{ + std::map strings; + std::map> vecStrings; + std::map 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; + } +}; diff --git a/src/FileUtils.h b/src/FileUtils.h index 89fb867..e5905d8 100644 --- a/src/FileUtils.h +++ b/src/FileUtils.h @@ -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) diff --git a/src/HFileGen.cpp b/src/HFileGen.cpp index da6be3d..bf41b12 100644 --- a/src/HFileGen.cpp +++ b/src/HFileGen.cpp @@ -3,11 +3,11 @@ #include "FileUtils.h" #include -void HFileGen::Create(const ConfigFile& conf) +void HFileGen::Create(ConfigFile& conf) { std::set hFiles; std::vector 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; diff --git a/src/HFileGen.h b/src/HFileGen.h index 03585a6..e0ce193 100644 --- a/src/HFileGen.h +++ b/src/HFileGen.h @@ -5,5 +5,5 @@ class HFileGen { public: - static void Create(const ConfigFile& conf); + static void Create(ConfigFile& conf); }; diff --git a/src/IncludeDeps.h b/src/IncludeDeps.h index 065b9b9..3ba496c 100755 --- a/src/IncludeDeps.h +++ b/src/IncludeDeps.h @@ -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 << " "; diff --git a/src/Makefile.cpp b/src/Makefile.cpp index 6243afe..ecc13b9 100755 --- a/src/Makefile.cpp +++ b/src/Makefile.cpp @@ -6,7 +6,7 @@ #include #include -void Makefile::Save(const ConfigFile& conf, unsigned int flags) +void Makefile::Save(ConfigFile& conf, unsigned int flags) { std::set hFiles; // hFile, directory std::set 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& 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& 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& 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& 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& 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& 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 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 "; diff --git a/src/Makefile.h b/src/Makefile.h index 6191711..36b589b 100755 --- a/src/Makefile.h +++ b/src/Makefile.h @@ -5,5 +5,5 @@ class Makefile { public: - static void Save(const ConfigFile& conf, unsigned int flags); + static void Save(ConfigFile& conf, unsigned int flags); }; diff --git a/src/Utils.cpp b/src/Utils.cpp index df74266..c4eace0 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -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& cppFiles) +void Utils::GetCppFiles(ConfigFile& conf, std::set& cppFiles) { std::vector 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& cppFiles) } } -void Utils::GetCppAndHFiles(const ConfigFile& conf, std::set& hFiles, std::set& cppFiles) +void Utils::GetCppAndHFiles(ConfigFile& conf, std::set& hFiles, std::set& cppFiles) { std::vector 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& hFiles, std } } - for(size_t i = 0; i < conf.dependencies.size(); ++i) + std::vector& 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& hFiles) +void Utils::GetHFiles(const std::string& dependencyDir, ConfigFile& conf, std::set& 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& 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 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)}); } } } diff --git a/src/Utils.h b/src/Utils.h index 572f539..85e1ee4 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -1,7 +1,5 @@ #pragma once -#include "ConfigFile.h" - #include #include @@ -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& cppFiles); - static void GetCppAndHFiles(const ConfigFile& conf, std::set& hFiles, std::set& cppFiles); - static void GetHFiles(const std::string& dependencyDir, const ConfigFile& conf, std::set& hFiles); + static void GetCppFiles(ConfigFile& conf, std::set& cppFiles); + static void GetCppAndHFiles(ConfigFile& conf, std::set& hFiles, std::set& cppFiles); + static void GetHFiles(const std::string& dependencyDir, ConfigFile& conf, std::set& hFiles); // Used for parsing xml static bool IsWhiteSpace(char c); diff --git a/src/compatibility/ConfigFileConf.cpp b/src/compatibility/ConfigFileConf.cpp index f5d45c1..22f61f5 100644 --- a/src/compatibility/ConfigFileConf.cpp +++ b/src/compatibility/ConfigFileConf.cpp @@ -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>{}); + + // 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>{}); + 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; } diff --git a/src/compatibility/ConfigFileConf.h b/src/compatibility/ConfigFileConf.h index 1bf3f4b..cd0a7f8 100644 --- a/src/compatibility/ConfigFileConf.h +++ b/src/compatibility/ConfigFileConf.h @@ -29,6 +29,6 @@ class ConfigFileConf public: ConfigFileConf(); - static ConfigFile Load(const std::string& filename); + static void CreateXMLFile(const std::string& filename); private: }; diff --git a/src/main.cpp b/src/main.cpp index ea1f7ae..e29877e 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -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& dependencies = conf.GetSettingVectorString(ConfigSetting::Dependency); + for(size_t i = 0;i& XMLObject::GetObject(const std::string& name, const std::vector& defaults) const +std::vector* 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>& 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& GetObject(const std::string& name, const std::vector& defaults = {}) const; + std::vector* GetObjectPtr(const std::string& name); const std::map>& 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.