From 7a627893824b3abd26bbc411fe38e59351450fe9 Mon Sep 17 00:00:00 2001 From: Thraix Date: Fri, 11 Oct 2019 22:00:20 +0200 Subject: [PATCH 1/4] Port XML from Greet-Engine --- Makefile | 22 ++- src/Utils.cpp | 29 ++++ src/Utils.h | 6 + src/xml/XML.cpp | 35 +++++ src/xml/XML.h | 9 ++ src/xml/XMLException.h | 22 +++ src/xml/XMLObject.cpp | 335 +++++++++++++++++++++++++++++++++++++++++ src/xml/XMLObject.h | 59 ++++++++ 8 files changed, 509 insertions(+), 8 deletions(-) create mode 100644 src/xml/XML.cpp create mode 100644 src/xml/XML.h create mode 100644 src/xml/XMLException.h create mode 100644 src/xml/XMLObject.cpp create mode 100644 src/xml/XMLObject.h diff --git a/Makefile b/Makefile index bc32c18..a63c9fc 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# This Makefile was generated using MakeGen v1.1.8 made by Tim Håkansson +# This Makefile was generated using MakeGen v1.2.0 made by Tim Håkansson # and is licensed under MIT. Full source of the project can be found at # https://github.com/Thraix/MakeGen CC=@g++ @@ -7,7 +7,7 @@ MKDIR_P=mkdir -p BIN=bin/ OBJPATH=$(BIN)intermediates INCLUDES= -OBJECTS=$(OBJPATH)/ConfigCLI.o $(OBJPATH)/ConfigFile.o $(OBJPATH)/HFileGen.o $(OBJPATH)/IncludeDeps.o $(OBJPATH)/Makefile.o $(OBJPATH)/Utils.o $(OBJPATH)/main.o +OBJECTS=$(OBJPATH)/ConfigCLI.o $(OBJPATH)/ConfigFile.o $(OBJPATH)/HFileGen.o $(OBJPATH)/IncludeDeps.o $(OBJPATH)/Makefile.o $(OBJPATH)/Utils.o $(OBJPATH)/main.o $(OBJPATH)/XML.o $(OBJPATH)/XMLObject.o CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3 -D_DEBUG LIBDIR= LDFLAGS= @@ -34,23 +34,29 @@ install: all $(info Installing MakeGen to /usr/bin/) @cp $(OUTPUT) /usr/bin/makegen $(OBJPATH)/ConfigCLI.o : src/ConfigCLI.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h - $(info -[14%]- $<) + $(info -[11%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/ConfigFile.o : src/ConfigFile.cpp src/ConfigFile.h src/FileUtils.h src/Common.h src/Utils.h - $(info -[28%]- $<) + $(info -[22%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/HFileGen.o : src/HFileGen.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h src/HFileGen.h - $(info -[42%]- $<) + $(info -[33%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/IncludeDeps.o : src/IncludeDeps.cpp src/Common.h src/IncludeDeps.h src/ConfigFile.h src/FileUtils.h src/Utils.h - $(info -[57%]- $<) + $(info -[44%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/Makefile.o : src/Makefile.cpp src/IncludeDeps.h src/ConfigFile.h src/FileUtils.h src/Common.h src/Utils.h src/Makefile.h - $(info -[71%]- $<) + $(info -[55%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/Utils.o : src/Utils.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h - $(info -[85%]- $<) + $(info -[66%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/main.o : src/main.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h src/FileUtils.h src/Utils.h src/HFileGen.h src/Makefile.h src/Timer.h + $(info -[77%]- $<) + $(CC) $(CFLAGS) -o $@ $< +$(OBJPATH)/XML.o : src/xml/XML.cpp src/xml/XML.h src/xml/XMLObject.h src/xml/XMLException.h + $(info -[88%]- $<) + $(CC) $(CFLAGS) -o $@ $< +$(OBJPATH)/XMLObject.o : src/xml/XMLObject.cpp src/xml/XMLException.h src/xml/XMLObject.h $(info -[100%]- $<) $(CC) $(CFLAGS) -o $@ $< diff --git a/src/Utils.cpp b/src/Utils.cpp index 899559f..df74266 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -95,3 +95,32 @@ void Utils::GetHFiles(const std::string& dependencyDir, const ConfigFile& conf, } } } +bool Utils::IsWhiteSpace(char c) +{ + return c == '\n' || c == '\t' || c == '\r' || c == ' ' || c == '\t'; +} + +bool Utils::IsLetter(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +bool Utils::IsWord(const std::string& string) +{ + for(auto it{string.begin()}; it != string.end();++it) + { + if(!IsLetter(*it)) + return false; + } + return true; +} + +std::string Utils::GetWord(const std::string& string, int startPos) +{ + if (startPos >= string.length()) + throw std::runtime_error("start position out of bounds."); + + int endPos = startPos; + while (endPos < string.length() && IsLetter(string[endPos])) endPos++; + return string.substr(startPos, endPos - startPos); +} diff --git a/src/Utils.h b/src/Utils.h index cacedac..572f539 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -30,4 +30,10 @@ struct Utils 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); + + // Used for parsing xml + static bool IsWhiteSpace(char c); + static bool IsLetter(char c); + static bool IsWord(const std::string& string); + static std::string GetWord(const std::string& string, int startPos = 0); }; diff --git a/src/xml/XML.cpp b/src/xml/XML.cpp new file mode 100644 index 0000000..9e8b807 --- /dev/null +++ b/src/xml/XML.cpp @@ -0,0 +1,35 @@ +#include "XML.h" + +#include "XMLException.h" + +#include +#include + +XMLObject XML::FromString(const std::string& string, const std::string& filename="") +{ + int startLine = 1; + int startPos = 0; + // Remove version tag. + if(string.find("") + 3; + startLine = std::count(string.begin(), string.begin()+startPos, '\n') + 1; + } + return XMLObject(string, startPos, startLine,filename); +} + +XMLObject XML::FromFile(const std::string& filename) +{ + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if(!file) + throw XMLException("Could not read file \""+filename+"\""); + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + std::string buffer; + buffer.reserve(size); + while (!file.eof()) + { + buffer += file.get(); + } + return FromString(buffer, filename); +} diff --git a/src/xml/XML.h b/src/xml/XML.h new file mode 100644 index 0000000..2bf9b5c --- /dev/null +++ b/src/xml/XML.h @@ -0,0 +1,9 @@ +#pragma once + +#include "XMLObject.h" + +struct XML +{ + static XMLObject FromString(const std::string& string, const std::string& filename); + static XMLObject FromFile(const std::string& fileName); +}; diff --git a/src/xml/XMLException.h b/src/xml/XMLException.h new file mode 100644 index 0000000..ade26f9 --- /dev/null +++ b/src/xml/XMLException.h @@ -0,0 +1,22 @@ +#pragma once + +#include "XMLObject.h" + +#include +#include + +class XMLException : public std::exception +{ + private: + std::string m_message; + public: + explicit XMLException(const std::string& message) : m_message("XMLException: " + message) {} + explicit XMLException(const std::string& message, const XMLObject::XMLLoadData& data) + : m_message("XMLException(" + data.file + ":" + std::to_string(data.line) + "): " + message) + {} + + virtual const char* what() const throw() + { + return m_message.c_str(); + } +}; diff --git a/src/xml/XMLObject.cpp b/src/xml/XMLObject.cpp new file mode 100644 index 0000000..649214e --- /dev/null +++ b/src/xml/XMLObject.cpp @@ -0,0 +1,335 @@ +#include "XMLObject.h" + +#include "../Common.h" +#include "../Utils.h" + +#include +#include "XMLException.h" + +XMLObject::XMLObject(const std::string& string) +{ + int pos = 0; + int line = 1; + XMLLoadData data{pos, line, ""}; + if(!ReadHead(string, data)) + ReadBodyTail(string, data); +} + +XMLObject::XMLObject(const std::string& string, int pos, int line, const std::string& file) +{ + XMLLoadData data{pos, line, file}; + if (!ReadHead(string, data)) + ReadBodyTail(string, data); +} + +XMLObject::XMLObject(const std::string& string, XMLLoadData& data) +{ + if (!ReadHead(string, data)) + ReadBodyTail(string, data); +} + +XMLObject::XMLObject(const std::string& name, const std::map& attributes, const std::string& text) + :name(name), attributes(attributes), text(text) +{ + +} + +XMLObject::XMLObject(const std::string& name, const std::map& attributes, const std::vector& objects) + : name(name), attributes(attributes), objects(objects) +{ + +} + +bool XMLObject::HasAttribute(const std::string& property) const +{ + return attributes.find(property) != attributes.end(); +} + +const std::string& XMLObject::GetAttribute(const std::string& property) const +{ + auto it = attributes.find(property); + if (it == attributes.end()) + throw XMLException((std::string("Attribute could not be found \"") + property + "\".").c_str()); + return it->second; +} + +const std::string& XMLObject::GetAttribute(const std::string& property, const std::string& defaultValue) const +{ + auto it = attributes.find(property); + if (it == attributes.end()) + return defaultValue; + return it->second; +} + +unsigned int XMLObject::GetObjectCount() const +{ + return objects.size(); +} + +const XMLObject& XMLObject::GetObject(unsigned int i) const +{ + if (i >= objects.size()) + throw XMLException((std::string("XML index out of bounds \"") + std::to_string(i) + "\".").c_str()); + + return objects[i]; +} + +const std::vector& XMLObject::GetObjects() const +{ + return objects; +} + +const std::string& XMLObject::GetName() const +{ + return name; +} + +const std::string& XMLObject::GetText() const +{ + return text; +} + +void XMLObject::SetName(const std::string& name) +{ + if(Utils::IsWord(name)) + this->name = name; + else + LOG_ERROR("XML Head can only be made up of letters"); +} + +void XMLObject::SetText(const std::string& text) +{ + this->text = text; +} + +void XMLObject::AddAttribute(const std::string& property, const std::string& value) +{ + if(Utils::IsWord(property)) + attributes.emplace(property, value); + else + LOG_ERROR("XML property name can only be made up of letters"); + +} + +XMLObject XMLObject::GetStrippedXMLObject() const +{ + if(text == "") + return XMLObject(name, attributes, objects); + else + return XMLObject(name, attributes, text); +} + +//////////////////////////////////////////////////////////// +// // +// Everything below here handles the reading of xml files // +// // +//////////////////////////////////////////////////////////// + + +bool XMLObject::ReadHead(const std::string& string, XMLLoadData& data) +{ + // Check if the first character is the start of and xml tag. + ReadWhiteSpace(string, data); + if (string[data.pos] != '<') + throw XMLException("Not an XML Object.", data); + + // Check if there is a closing tag + size_t closing = string.find('>'); + if (closing == std::string::npos) + throw XMLException("No enclosing > for opening tag.", data); + + // Read the name of the tag + ReadName(string, data); + + // Read all attributes of the xml tag + ReadAttributes(string, data); + + // Read opening tag + if (string[data.pos] == '/') + { + data.pos++; + ReadWhiteSpace(string, data); + if (string[data.pos] != '>') + throw XMLException((std::string("Invalid character proceeding / in opening XML Tag \"") + string[data.pos] + "\".").c_str(), data); + data.pos++; + // nothing more to read. + return true; + } + + ReadWhiteSpace(string, data); + if (string[data.pos] != '>') + throw XMLException((std::string("Invalid character proceeding attributes in opening XML Tag \"") + string[data.pos] + "\".").c_str(), data); + (data.pos)++; + return false; +} + +void XMLObject::ReadName(const std::string& string, XMLLoadData& data) +{ + data.pos++; + ReadWhiteSpace(string, data); + if (!Utils::IsLetter(string[data.pos])) + throw XMLException("Invalid XML name. Can only contain letters.", data); + name = Utils::GetWord(string, data.pos); + data.pos += name.length(); + ReadWhiteSpace(string, data); + if (string[data.pos] != '/' && string[data.pos] != '>' && Utils::IsWhiteSpace(string[data.pos])) + { + throw XMLException((std::string("Invalid character proceeding name in XML Tag \"") + string[data.pos] + "\".").c_str(), data); + } +} + +void XMLObject::ReadAttributes(const std::string& string, XMLLoadData& data) +{ + ReadWhiteSpace(string, data); + + while (string[data.pos] != '>' && string[data.pos] != '/') + { + ReadAttribute(string, data); + } +} + +void XMLObject::ReadAttribute(const std::string& string, XMLLoadData& data) +{ + // Read property name + std::string property = ReadXMLName(string, data); + if (property.length() == 0) + throw XMLException((std::string("Invalid character proceeding name \"") + string[data.pos] + "\".").c_str(), data); + if (attributes.count(property) > 0) + throw XMLException((std::string("Duplicate property in XML tag \"") + property + "\".").c_str(), data); + data.pos += property.length(); + ReadWhiteSpace(string, data); + + // Read = + if (string[data.pos] != '=') + throw XMLException((std::string("Invalid character proceeding property name in XML Tag \"") + string[data.pos] + "\".").c_str(), data); + (data.pos)++; + ReadWhiteSpace(string, data); + + // Read value + if (string[data.pos] != '\"') + throw XMLException("XML property value is not inside enclosing quotes.", data); + (data.pos)++; + int valueStart = data.pos; + while (string[data.pos] != '\"') (data.pos)++; + std::string value = string.substr(valueStart, (data.pos) - valueStart); + ReplacePredefinedEntities(value, data); + (data.pos)++; + attributes.emplace(property, value); + ReadWhiteSpace(string, data); +} + +void XMLObject::ReadBodyTail(const std::string& string, XMLLoadData& data) +{ + ReadWhiteSpace(string, data); + if (string[data.pos] != '<') + { + ReadText(string, data); + ReadWhiteSpace(string, data); + std::string closeTag = GetClosingTag(string, data); + if (closeTag.length() == 0) + throw XMLException("Tag after XML Test was not a closing tag. XMLObject doesn't support text and other XMLObjects at the same time.", data); + return; + } + // Check if we can read the closing tag. + std::string closeTag = GetClosingTag(string, data); + while (closeTag.length() == 0) + { + XMLObject object = XMLObject(string, data); + objects.push_back(object); + ReadWhiteSpace(string, data); + closeTag = GetClosingTag(string, data); + } +} + +void XMLObject::ReadText(const std::string& string, XMLLoadData& data) +{ + int startPos = data.pos; + while (string[data.pos] != '<') (data.pos)++; + text = string.substr(startPos, (data.pos) - startPos); + ReplacePredefinedEntities(text, data); +} + +void XMLObject::ReadWhiteSpace(const std::string& string, XMLLoadData& data) +{ + while (Utils::IsWhiteSpace(string[data.pos])) { + if (string[data.pos] == '\n') + (data.line)++; + (data.pos)++; + } +} + + +std::string XMLObject::GetClosingTag(const std::string& string, XMLLoadData& data) +{ + int startPos = data.pos; + int startLine = data.line; + if (string[(data.pos)++] != '<') + { + data.pos = startPos; + data.line = startLine; + return ""; + } + ReadWhiteSpace(string, data); + if (string[(data.pos)++] != '/') + { + data.pos = startPos; + data.line = startLine; + return ""; + } + ReadWhiteSpace(string, data); + std::string tag = Utils::GetWord(string, data.pos); + if (tag != name) + throw XMLException((std::string("Closing tag doesn't match opening tag. (\"") + name + "\" != \"" + tag+ "\")").c_str(), data); + data.pos += tag.length(); + ReadWhiteSpace(string, data); + if (string[data.pos] != '>') + throw XMLException((std::string("Invalid character in closing tag \"") + string[data.pos] + "\".").c_str(), data); + (data.pos)++; + return string.substr(startPos, (data.pos) - startPos); +} + +void XMLObject::ReplacePredefinedEntities(std::string& string, XMLLoadData& data) +{ + std::vector> entities + { + {""","\""}, + {"'", "\'"}, + {"<", "<"}, + {">",">"}, + {"&", "&"} + }; + size_t pos = string.find('&'); + while(pos != std::string::npos) + { + bool found = false; + for(auto entity : entities) + { + if(strncmp(&string[pos], entity.first.c_str(), entity.first.length()) == 0) + { + string.replace(pos, entity.first.length(), entity.second); + found = true; + } + } + if(!found) + LOG_ERROR("(" + data.file + ":" + std::to_string(data.line) + "): ""Ampersand found in xml but isn't a predefined entity."); + pos = string.find('&', pos+1); + } +} + +std::string XMLObject::ReadXMLName(const std::string& string, XMLLoadData& data) +{ + if(!(Utils::IsLetter(string[data.pos]) || + string[data.pos] == '_' || + string[data.pos] == ':')) + throw XMLException(std::string("Name doesn't start with a letter."), data); + + int endPos = data.pos + 1; + while (endPos < string.length() && ( + Utils::IsLetter(string[endPos]) || + string[endPos] == '_' || + string[endPos] == '-' || + string[endPos] == ':' || + string[endPos] == '.')) + endPos++; + return string.substr(data.pos, endPos - data.pos); +} diff --git a/src/xml/XMLObject.h b/src/xml/XMLObject.h new file mode 100644 index 0000000..6a2d2ff --- /dev/null +++ b/src/xml/XMLObject.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +class XMLObject +{ + public: + friend class XMLexception; + struct XMLLoadData + { + int pos; + int line; + const std::string& file; + }; + private: + std::string name; + std::string text; + + std::map attributes; + std::vector objects; + + public: + XMLObject() {} + XMLObject(const std::string& string); + XMLObject(const std::string& string, int pos, int line, const std::string& file); + XMLObject(const std::string& string, XMLLoadData& data); + XMLObject(const std::string& name, const std::map& properties, const std::string& text); + XMLObject(const std::string& name, const std::map& properties, const std::vector& objects); + + bool HasAttribute(const std::string& property) const; + const std::string& GetAttribute(const std::string& property) const; + const std::string& GetAttribute(const std::string& property, const std::string& defaultValue) const; + + unsigned int GetObjectCount() const; + const XMLObject& GetObject(unsigned int i) const; + const std::vector& GetObjects() const; + const std::string& GetName() const; + const std::string& GetText() const; + XMLObject GetStrippedXMLObject() const; + + void SetName(const std::string& name); + void SetText(const std::string& text); + void AddAttribute(const std::string& property, const std::string& value); + + private: + std::string GetClosingTag(const std::string& string, XMLLoadData& data); + // Returns true if the head contained closing tag. + bool ReadHead(const std::string& string, XMLLoadData& data); + void ReadName(const std::string& string, XMLLoadData& data); + void ReadAttribute(const std::string& string, XMLLoadData& data); + void ReadAttributes(const std::string& string, XMLLoadData& data); + void ReadBodyTail(const std::string& string, XMLLoadData& data); + void ReadText(const std::string& string, XMLLoadData& data); + void ReadWhiteSpace(const std::string& string, XMLLoadData& data); + void ReplacePredefinedEntities(std::string& string, XMLLoadData& data); + std::string ReadXMLName(const std::string& string, XMLLoadData& data); +}; From f3ab790912783d64ea399b8c1afb76c73c75e72c Mon Sep 17 00:00:00 2001 From: Thraix Date: Sun, 13 Oct 2019 19:59:43 +0200 Subject: [PATCH 2/4] Read and write config as xml --- Makefile | 35 +-- makegen.conf | 19 -- makegen.xml | 14 ++ src/Common.h | 2 +- src/ConfigFile.cpp | 307 ++++++++++++--------------- src/ConfigFile.h | 8 +- src/compatibility/ConfigFileConf.cpp | 164 ++++++++++++++ src/compatibility/ConfigFileConf.h | 34 +++ src/xml/XMLObject.cpp | 68 +++++- src/xml/XMLObject.h | 21 +- 10 files changed, 452 insertions(+), 220 deletions(-) delete mode 100644 makegen.conf create mode 100644 makegen.xml create mode 100644 src/compatibility/ConfigFileConf.cpp create mode 100644 src/compatibility/ConfigFileConf.h diff --git a/Makefile b/Makefile index a63c9fc..af90f28 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ MKDIR_P=mkdir -p BIN=bin/ OBJPATH=$(BIN)intermediates INCLUDES= -OBJECTS=$(OBJPATH)/ConfigCLI.o $(OBJPATH)/ConfigFile.o $(OBJPATH)/HFileGen.o $(OBJPATH)/IncludeDeps.o $(OBJPATH)/Makefile.o $(OBJPATH)/Utils.o $(OBJPATH)/main.o $(OBJPATH)/XML.o $(OBJPATH)/XMLObject.o +OBJECTS=$(OBJPATH)/ConfigCLI.o $(OBJPATH)/ConfigFile.o $(OBJPATH)/HFileGen.o $(OBJPATH)/IncludeDeps.o $(OBJPATH)/Makefile.o $(OBJPATH)/Utils.o $(OBJPATH)/ConfigFileConf.o $(OBJPATH)/main.o $(OBJPATH)/XML.o $(OBJPATH)/XMLObject.o CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3 -D_DEBUG LIBDIR= LDFLAGS= @@ -33,29 +33,32 @@ $(OUTPUT): $(OBJECTS) install: all $(info Installing MakeGen to /usr/bin/) @cp $(OUTPUT) /usr/bin/makegen -$(OBJPATH)/ConfigCLI.o : src/ConfigCLI.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h - $(info -[11%]- $<) +$(OBJPATH)/ConfigCLI.o : src/ConfigCLI.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h src/xml/XMLObject.h + $(info -[10%]- $<) $(CC) $(CFLAGS) -o $@ $< -$(OBJPATH)/ConfigFile.o : src/ConfigFile.cpp src/ConfigFile.h src/FileUtils.h src/Common.h src/Utils.h - $(info -[22%]- $<) +$(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 + $(info -[20%]- $<) $(CC) $(CFLAGS) -o $@ $< -$(OBJPATH)/HFileGen.o : src/HFileGen.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h src/HFileGen.h - $(info -[33%]- $<) +$(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 + $(info -[30%]- $<) $(CC) $(CFLAGS) -o $@ $< -$(OBJPATH)/IncludeDeps.o : src/IncludeDeps.cpp src/Common.h src/IncludeDeps.h src/ConfigFile.h src/FileUtils.h src/Utils.h - $(info -[44%]- $<) +$(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 + $(info -[40%]- $<) $(CC) $(CFLAGS) -o $@ $< -$(OBJPATH)/Makefile.o : src/Makefile.cpp src/IncludeDeps.h src/ConfigFile.h src/FileUtils.h src/Common.h src/Utils.h src/Makefile.h - $(info -[55%]- $<) +$(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 + $(info -[50%]- $<) $(CC) $(CFLAGS) -o $@ $< -$(OBJPATH)/Utils.o : src/Utils.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h - $(info -[66%]- $<) +$(OBJPATH)/Utils.o : src/Utils.cpp src/FileUtils.h src/Common.h src/Utils.h src/ConfigFile.h src/xml/XMLObject.h + $(info -[60%]- $<) $(CC) $(CFLAGS) -o $@ $< -$(OBJPATH)/main.o : src/main.cpp src/Common.h src/ConfigCLI.h src/ConfigFile.h src/FileUtils.h src/Utils.h src/HFileGen.h src/Makefile.h src/Timer.h - $(info -[77%]- $<) +$(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 + $(info -[80%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/XML.o : src/xml/XML.cpp src/xml/XML.h src/xml/XMLObject.h src/xml/XMLException.h - $(info -[88%]- $<) + $(info -[90%]- $<) $(CC) $(CFLAGS) -o $@ $< $(OBJPATH)/XMLObject.o : src/xml/XMLObject.cpp src/xml/XMLException.h src/xml/XMLObject.h $(info -[100%]- $<) diff --git a/makegen.conf b/makegen.conf deleted file mode 100644 index e445d05..0000000 --- a/makegen.conf +++ /dev/null @@ -1,19 +0,0 @@ -#libs -#libdirs -#includedirs -#defines -_DEBUG -#compileflags -#dependencies -#srcdir -src/ -#outputdir -bin/ -#projectname -MakeGen -#outputname -makegen -#executable -true -#generatehfile -false diff --git a/makegen.xml b/makegen.xml new file mode 100644 index 0000000..9cb4cbf --- /dev/null +++ b/makegen.xml @@ -0,0 +1,14 @@ + + + _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 6dea76f..2790775 100755 --- a/src/Common.h +++ b/src/Common.h @@ -10,7 +10,7 @@ // Major changes, probably not be backwards compatible #define MAKEGEN_VERSION_MAJOR 1 // Release, should be backwards compatible with any minor version -#define MAKEGEN_VERSION_RELEASE 2 +#define MAKEGEN_VERSION_RELEASE 3 // Minor changes, should be compatible with any other minor version with same major and release. #define MAKEGEN_VERSION_MINOR 0 #define MAKEGEN_VERSION ("v" STR(MAKEGEN_VERSION_MAJOR) "." STR(MAKEGEN_VERSION_RELEASE) "." STR(MAKEGEN_VERSION_MINOR)) diff --git a/src/ConfigFile.cpp b/src/ConfigFile.cpp index 5347bee..be7b7a1 100755 --- a/src/ConfigFile.cpp +++ b/src/ConfigFile.cpp @@ -1,17 +1,14 @@ #include "ConfigFile.h" #include "FileUtils.h" +#include "compatibility/ConfigFileConf.h" +#include "xml/XML.h" #include #include -#define FLAG_NONE 0 -#define FLAG_VECTOR 1 -#define FLAG_STRING 2 -#define FLAG_BOOL 3 - ConfigFile::ConfigFile() - : outputdir("bin/"), srcdir("src/"), outputname(""), projectname(FileUtils::GetCurrentDirectory()), hFile(""), executable(true), shared(true), generateHFile(false) + : outputdir("bin/"), srcdir("src/"), outputname(""), projectname(FileUtils::GetCurrentDirectory()), hFile(projectname+".h"), executable(true), shared(true), generateHFile(false) { // Converts project name (current directory) to lowercase // and replace whitespace with underscore @@ -54,27 +51,42 @@ std::optional ConfigFile::GetConfigFile(const std::string& filepath, return {}; } + bool oldFile = false; std::ifstream f(filepath + CONFIG_FILENAME); + if(!f.good()) + { + // try to read an old config file + f.close(); + f = std::ifstream(filepath + "makegen.conf"); + oldFile = true; + } + // Check if the file exists if(f.good()) { f.close(); - ConfigFile conf = ConfigFile::Load(realPath); - loadedConfigs.emplace(realPath, conf); + std::optional conf; + if(oldFile) + conf = ConfigFileConf::Load(realPath); + else + conf = ConfigFile::Load(realPath); + if(!conf) + return {}; + loadedConfigs.emplace(realPath, *conf); // Create dependency config files. - for(size_t i = 0; i < conf.dependencies.size();++i) + for(size_t i = 0; i < conf->dependencies.size();++i) { - std::optional dep = GetConfigFile(conf.configPath + conf.dependencies[i], loadedConfigs); + std::optional dep = GetConfigFile(conf->configPath + conf->dependencies[i], loadedConfigs); if(dep) { - conf.dependencyConfigs.push_back(*dep); - conf.dependencies[i] = dep->configPath; + conf->dependencyConfigs.push_back(*dep); + conf->dependencies[i] = dep->configPath; } else { // Remove the dependency since it is already accounted for - conf.dependencies.erase(conf.dependencies.begin() + i); + conf->dependencies.erase(conf->dependencies.begin() + i); --i; } } @@ -84,120 +96,99 @@ std::optional ConfigFile::GetConfigFile(const std::string& filepath, } -ConfigFile ConfigFile::Load(const std::string& filepath) +std::optional ConfigFile::Load(const std::string& filedir) { - ConfigFile conf; - conf.configPath = filepath; - unsigned int loadFlag = 0; + XMLObject object{XML::FromFile(filedir + CONFIG_FILENAME)}; - std::vector* vec; - std::string* s; - bool* b; - - bool isDirectory = false; - - std::ifstream file(filepath+CONFIG_FILENAME); - std::string line; - - if(file.is_open()) + 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) { - // config name, { pointer to memory, isDirectory} - std::map> strings = + if(!it->HasAttribute("name")) { - {"#srcdir", {&conf.srcdir, true}}, - {"#outputdir", {&conf.outputdir, true}}, - {"#outputname", {&conf.outputname, false}}, - {"#projectname", {&conf.projectname, false}}, - {"#hfile", {&conf.hFile, false}}, - }; + LOG_ERROR("No name attribute in configuration tag"); + continue; + } + if(it->GetAttribute("name") == target) + { + configuration = &(*it); + break; + } + } - // config name, { pointer to memory, isDirectory} - std::map*, bool>> vectors = - { - {"#libs", {&conf.libs, false}}, - {"#libdirs", {&conf.libdirs, true}}, - {"#includedirs", {&conf.includedirs, true}}, - {"#compileflags", {&conf.flags, false}}, - {"#defines", {&conf.defines, false}}, - {"#dependencies", {&conf.dependencies, true}}, - }; + if(configuration == nullptr) + { + LOG_ERROR("No configuration matching target: ", target); + return {}; + } - std::map booleans = - { - {"#executable", &conf.executable}, - {"#shared", &conf.shared}, - {"#generatehfile", &conf.generateHFile}, - }; + 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(); - while(std::getline(file,line)) + 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(line == "") - continue; - if(line[0]=='#') + if(it->GetText() != "") { - // The format is a bit wacky, but it is this way since we do not want - // to use map::find for all maps. This way we gain some optimization. - auto&& itStr{strings.find(line)}; - if(itStr != strings.end()) - { - s = itStr->second.first; - isDirectory = itStr->second.second; - loadFlag = FLAG_STRING; - } - else - { - auto&& itVec{vectors.find(line)}; - if(itVec != vectors.end()) - { - vec = itVec->second.first; - isDirectory = itVec->second.second; - loadFlag = FLAG_VECTOR; - } - else - { - auto&& itBool{booleans.find(line)}; - if(itBool != booleans.end()) - { - b = itBool->second; - loadFlag = FLAG_BOOL; - } - else - { - LOG_ERROR("Invalid flag: ", line); - loadFlag = FLAG_NONE; - } - } - } - } - else - { - if(loadFlag == FLAG_STRING) - { - if(isDirectory && line[line.size()-1] != '/') - line += '/'; - *s = line; - } - else if(loadFlag == FLAG_VECTOR) - { - if(isDirectory && line[line.size()-1] != '/') - {; - line += '/'; - } - vec->push_back(line); - } - else if(loadFlag == FLAG_BOOL) - { - if(line == "true") - *b = true; - else - *b = false; - } + std::string s = it->GetText(); + if(isDir && s[s.size()-1] != '/') + s += '/'; + vec->push_back(s); } } } - if(conf.hFile == "") - conf.hFile = conf.projectname+".h"; - return conf; } @@ -279,58 +270,36 @@ ConfigFile ConfigFile::Gen() void ConfigFile::Save() const { - std::ofstream file("makegen.conf"); - file << "#libs" << std::endl; - for(auto it = libs.begin();it!=libs.end();++it) - { - file << *it << std::endl; - } - file << "#libdirs" << std::endl; - for(auto it = libdirs.begin();it!=libdirs.end();++it) - { - file << *it << std::endl; - } - file << "#includedirs" << std::endl; - for(auto it = includedirs.begin();it!=includedirs.end();++it) - { - file << *it << std::endl; - } - file << "#defines" << std::endl; - for(auto it = defines.begin();it!=defines.end();++it) - { - file << *it << std::endl; - } - file << "#compileflags" << std::endl; - for(auto it = flags.begin();it!=flags.end();++it) - { - file << *it << std::endl; - } - file << "#dependencies" << std::endl; - for(auto it = dependencies.begin();it!=dependencies.end();++it) - { - file << *it << std::endl; - } - file << "#srcdir" << std::endl; - file << srcdir << std::endl; - file << "#outputdir" << std::endl; - file << outputdir << std::endl; - file << "#projectname" << std::endl; - file << projectname << std::endl; - file << "#outputname" << std::endl; - file << outputname << std::endl; - file << "#executable" << std::endl; - file << (executable ? "true" : "false") << std::endl; - file << "#generatehfile" << std::endl; - file << (generateHFile ? "true" : "false") << std::endl; - if(generateHFile) - { - file << "#hfile" << std::endl; - file << hFile << std::endl; - } - if(!executable) - { - file << "#shared" << std::endl; - file << (shared ? "true" : "false") << std::endl; - } - file.close(); + 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", {}, projectname)); + configuration.AddXMLObject(XMLObject("outputname", {}, outputname)); + configuration.AddXMLObject(XMLObject("srcdir", {}, srcdir)); + configuration.AddXMLObject(XMLObject("outputdir", {}, outputdir)); + configuration.AddXMLObject(XMLObject("hfile", {}, hFile)); + configuration.AddXMLObject(XMLObject("outputtype", {}, + executable ? "executable" : (shared ? "sharedlibrary" : "staticlibrary"))); + configuration.AddXMLObject(XMLObject("generatehfile", {}, generateHFile ? "true" : "false")); + + for(auto it = libs.begin();it != libs.end(); ++it) + configuration.AddXMLObject({"library",{},*it}); + for(auto it = libdirs.begin();it != libdirs.end(); ++it) + configuration.AddXMLObject({"libdir",{},*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 = dependencies.begin();it != dependencies.end(); ++it) + configuration.AddXMLObject({"dependency",{},*it}); + + makegen.AddXMLObject(configuration); + std::ofstream file("makegen.xml"); + file << makegen; } diff --git a/src/ConfigFile.h b/src/ConfigFile.h index 3d9b36b..6e8cc71 100755 --- a/src/ConfigFile.h +++ b/src/ConfigFile.h @@ -1,16 +1,19 @@ #pragma once +#include "xml/XMLObject.h" + #include #include #include #include -static const std::string CONFIG_FILENAME = "makegen.conf"; +static const std::string CONFIG_FILENAME = "makegen.xml"; class ConfigFile { public: std::string configPath; + std::vector libs; std::vector libdirs; std::vector includedirs; @@ -26,6 +29,7 @@ class ConfigFile bool executable; bool shared; bool generateHFile; + std::vector dependencyConfigs; public: ConfigFile(); @@ -34,7 +38,7 @@ class ConfigFile static std::optional GetConfigFile(const std::string& filepath = "./"); private: static std::optional GetConfigFile(const std::string& filepath, std::map& loadedConfigs); - static ConfigFile Load(const std::string& filename); + static std::optional Load(const std::string& filename); static void InputBoolean(const std::string& inputText, bool& b); static void InputMultiple(const std::string& inputText, std::vector& vec, bool needEnding); static void InputString(const std::string& inputText, std::string& vec, bool needEnding, bool allowEmpty); diff --git a/src/compatibility/ConfigFileConf.cpp b/src/compatibility/ConfigFileConf.cpp new file mode 100644 index 0000000..f5d45c1 --- /dev/null +++ b/src/compatibility/ConfigFileConf.cpp @@ -0,0 +1,164 @@ +#include "ConfigFileConf.h" + +const std::string CONFIG_FILENAME_CONF = "makegen.conf"; + +#include "../ConfigFile.h" +#include "../FileUtils.h" + +#include +#include + +#define FLAG_NONE 0 +#define FLAG_VECTOR 1 +#define FLAG_STRING 2 +#define FLAG_BOOL 3 + +ConfigFileConf::ConfigFileConf() + : outputdir("bin/"), srcdir("src/"), outputname(""), projectname(FileUtils::GetCurrentDirectory()), hFile(""), executable(true), shared(true), generateHFile(false) +{ + // Converts project name (current directory) to lowercase + // and replace whitespace with underscore + std::transform( + projectname.begin(), + projectname.end(), + std::back_inserter(outputname), + [](unsigned char c) + { + if(c == ' ') + return '_'; + return (char)std::tolower(c); + }); + + // Removes all other characters + std::remove_if( + outputdir.begin(), + outputdir.end(), + [](unsigned char c) + { + return (c < 'a' || c > 'z') && c != '_'; + }); + + // Add suffix + outputname += ".out"; +} + +ConfigFile ConfigFileConf::Load(const std::string& filepath) +{ + ConfigFile conf; + conf.configPath = filepath; + unsigned int loadFlag = 0; + + std::vector* vec; + std::string* s; + bool* b; + + bool isDirectory = false; + + std::ifstream file(filepath + CONFIG_FILENAME_CONF); + std::string line; + + if(file.is_open()) + { + // config name, { pointer to memory, isDirectory} + std::map> strings = + { + {"#srcdir", {&conf.srcdir, true}}, + {"#outputdir", {&conf.outputdir, true}}, + {"#outputname", {&conf.outputname, false}}, + {"#projectname", {&conf.projectname, false}}, + {"#hfile", {&conf.hFile, false}}, + }; + + // config name, { pointer to memory, isDirectory} + std::map*, bool>> vectors = + { + {"#libs", {&conf.libs, false}}, + {"#libdirs", {&conf.libdirs, true}}, + {"#includedirs", {&conf.includedirs, true}}, + {"#compileflags", {&conf.flags, false}}, + {"#defines", {&conf.defines, false}}, + {"#dependencies", {&conf.dependencies, true}}, + }; + + std::map booleans = + { + {"#executable", &conf.executable}, + {"#shared", &conf.shared}, + {"#generatehfile", &conf.generateHFile}, + }; + + while(std::getline(file,line)) + { + if(line == "") + continue; + if(line[0]=='#') + { + // The format is a bit wacky, but it is this way since we do not want + // to use map::find for all maps. This way we gain some optimization. + auto&& itStr{strings.find(line)}; + if(itStr != strings.end()) + { + s = itStr->second.first; + isDirectory = itStr->second.second; + loadFlag = FLAG_STRING; + } + else + { + auto&& itVec{vectors.find(line)}; + if(itVec != vectors.end()) + { + vec = itVec->second.first; + isDirectory = itVec->second.second; + loadFlag = FLAG_VECTOR; + } + else + { + auto&& itBool{booleans.find(line)}; + if(itBool != booleans.end()) + { + b = itBool->second; + loadFlag = FLAG_BOOL; + } + else + { + LOG_ERROR("Invalid flag: ", line); + loadFlag = FLAG_NONE; + } + } + } + } + else + { + if(loadFlag == FLAG_STRING) + { + if(isDirectory && line[line.size()-1] != '/') + line += '/'; + *s = line; + } + else if(loadFlag == FLAG_VECTOR) + { + if(isDirectory && line[line.size()-1] != '/') + {; + line += '/'; + } + vec->push_back(line); + } + else if(loadFlag == FLAG_BOOL) + { + if(line == "true") + *b = true; + else + *b = false; + } + } + } + } + if(conf.hFile == "") + conf.hFile = conf.projectname+".h"; + + 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 new file mode 100644 index 0000000..1bf3f4b --- /dev/null +++ b/src/compatibility/ConfigFileConf.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include + +class ConfigFile; + +class ConfigFileConf +{ + public: + 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; + public: + ConfigFileConf(); + + static ConfigFile Load(const std::string& filename); + private: +}; diff --git a/src/xml/XMLObject.cpp b/src/xml/XMLObject.cpp index 649214e..4466325 100644 --- a/src/xml/XMLObject.cpp +++ b/src/xml/XMLObject.cpp @@ -34,7 +34,7 @@ XMLObject::XMLObject(const std::string& name, const std::map& attributes, const std::vector& objects) +XMLObject::XMLObject(const std::string& name, const std::map& attributes, const std::map>& objects) : name(name), attributes(attributes), objects(objects) { @@ -66,15 +66,16 @@ unsigned int XMLObject::GetObjectCount() const return objects.size(); } -const XMLObject& XMLObject::GetObject(unsigned int i) const +const std::vector& XMLObject::GetObject(const std::string& name, const std::vector& defaults) const { - if (i >= objects.size()) - throw XMLException((std::string("XML index out of bounds \"") + std::to_string(i) + "\".").c_str()); + auto it = objects.find(name); + if(it == objects.end()) + return defaults; - return objects[i]; + return it->second; } -const std::vector& XMLObject::GetObjects() const +const std::map>& XMLObject::GetObjects() const { return objects; } @@ -108,7 +109,14 @@ void XMLObject::AddAttribute(const std::string& property, const std::string& val attributes.emplace(property, value); else LOG_ERROR("XML property name can only be made up of letters"); - +} +void XMLObject::AddXMLObject(const XMLObject& object) +{ + auto it = objects.find(object.name); + if(it == objects.end()) + objects.emplace(object.name, std::vector{object}); + else + it->second.push_back(object); } XMLObject XMLObject::GetStrippedXMLObject() const @@ -234,8 +242,7 @@ void XMLObject::ReadBodyTail(const std::string& string, XMLLoadData& data) std::string closeTag = GetClosingTag(string, data); while (closeTag.length() == 0) { - XMLObject object = XMLObject(string, data); - objects.push_back(object); + AddXMLObject(XMLObject(string, data)); ReadWhiteSpace(string, data); closeTag = GetClosingTag(string, data); } @@ -333,3 +340,46 @@ std::string XMLObject::ReadXMLName(const std::string& string, XMLLoadData& data) endPos++; return string.substr(data.pos, endPos - data.pos); } + +std::ostream& XMLObject::WriteToStream(std::ostream& stream, int indent) const +{ + for(int i = 0;ifirst << "=\"" << it->second << "\""; + } + stream << ">"; + if(text != "") + { + stream << text; + } + else + { + bool hasChild = false; + for(auto it = objects.begin(); it != objects.end();++it) + { + for(auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) + { + stream << "\n"; + it2->WriteToStream(stream, indent+1); + hasChild = true; + } + } + if(hasChild) + { + stream << "\n"; + for(int i = 0;i"; + + return stream; +} + diff --git a/src/xml/XMLObject.h b/src/xml/XMLObject.h index 6a2d2ff..817efc2 100644 --- a/src/xml/XMLObject.h +++ b/src/xml/XMLObject.h @@ -2,6 +2,7 @@ #include #include +#include #include class XMLObject @@ -19,7 +20,7 @@ class XMLObject std::string text; std::map attributes; - std::vector objects; + std::map> objects; public: XMLObject() {} @@ -27,15 +28,15 @@ class XMLObject XMLObject(const std::string& string, int pos, int line, const std::string& file); XMLObject(const std::string& string, XMLLoadData& data); XMLObject(const std::string& name, const std::map& properties, const std::string& text); - XMLObject(const std::string& name, const std::map& properties, const std::vector& objects); + XMLObject(const std::string& name, const std::map& properties, const std::map>& objects); bool HasAttribute(const std::string& property) const; const std::string& GetAttribute(const std::string& property) const; const std::string& GetAttribute(const std::string& property, const std::string& defaultValue) const; unsigned int GetObjectCount() const; - const XMLObject& GetObject(unsigned int i) const; - const std::vector& GetObjects() const; + const std::vector& GetObject(const std::string& name, const std::vector& defaults = {}) const; + const std::map>& GetObjects() const; const std::string& GetName() const; const std::string& GetText() const; XMLObject GetStrippedXMLObject() const; @@ -43,6 +44,18 @@ class XMLObject void SetName(const std::string& name); void SetText(const std::string& text); void AddAttribute(const std::string& property, const std::string& value); + void AddXMLObject(const XMLObject& object); + + friend bool operator<(const XMLObject& obj1, const XMLObject& obj2) + { + return obj1.name < obj2.name; + } + + std::ostream& WriteToStream(std::ostream& stream, int indent = 0) const; + friend std::ostream& operator<<(std::ostream& stream, const XMLObject& object) + { + return object.WriteToStream(stream); + } private: std::string GetClosingTag(const std::string& string, XMLLoadData& data); From d9f7dc4d17a86817158c2045adf25e457ea07a29 Mon Sep 17 00:00:00 2001 From: Thraix Date: Fri, 18 Oct 2019 11:44:40 +0200 Subject: [PATCH 3/4] Rework how the config file is read Still need to remove reduntant code and test it much more thoroughly. --- Makefile | 18 +- makegen.xml | 16 +- src/Common.h | 21 +- src/ConfigCLI.cpp | 178 ++++----- src/ConfigCLI.h | 4 +- src/ConfigFile.cpp | 518 ++++++++++++++++++--------- src/ConfigFile.h | 52 ++- src/ConfigUtils.h | 242 +++++++++++++ src/FileUtils.h | 18 +- src/HFileGen.cpp | 8 +- src/HFileGen.h | 2 +- src/IncludeDeps.h | 2 +- src/Makefile.cpp | 56 +-- src/Makefile.h | 2 +- src/Utils.cpp | 25 +- src/Utils.h | 10 +- src/compatibility/ConfigFileConf.cpp | 50 ++- src/compatibility/ConfigFileConf.h | 2 +- src/main.cpp | 28 +- src/xml/XMLObject.cpp | 29 +- src/xml/XMLObject.h | 40 ++- 21 files changed, 934 insertions(+), 387 deletions(-) create mode 100644 src/ConfigUtils.h 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. From 95976c32c34d16d6f56ccf01e821818b7861208f Mon Sep 17 00:00:00 2001 From: Thraix Date: Fri, 25 Oct 2019 14:45:44 +0200 Subject: [PATCH 4/4] Fix dependency config generating in current folder --- src/compatibility/ConfigFileConf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compatibility/ConfigFileConf.cpp b/src/compatibility/ConfigFileConf.cpp index 22f61f5..f17d53b 100644 --- a/src/compatibility/ConfigFileConf.cpp +++ b/src/compatibility/ConfigFileConf.cpp @@ -188,7 +188,7 @@ void ConfigFileConf::CreateXMLFile(const std::string& filepath) configuration.AddXMLObject({"dependency",{},*it}); makegen.AddXMLObject(configuration); - std::ofstream xmlFile("makegen.xml"); + std::ofstream xmlFile(conf.configPath + "makegen.xml"); xmlFile << makegen; } }