Create system for generating Makefiles

This commit is contained in:
Thraix
2018-06-09 23:49:01 +02:00
parent c8d1ce47fe
commit f35a89e10b
12 changed files with 762 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
#include "ConfigFile.h"
#include <fstream>
#include "Logging.h"
#define FLAG_NONE 0
#define FLAG_VECTOR 1
#define FLAG_STRING 2
#define FLAG_BOOL 3
ConfigFile::ConfigFile()
: outputdir("bin"), outputname("out.a"),executable(true)
{
}
ConfigFile ConfigFile::Load()
{
ConfigFile conf;
unsigned int loadFlag = 0;
std::vector<std::string>* vec;
std::string* s;
bool* b;
std::ifstream file("makegen.conf");
std::string line;
while(std::getline(file,line))
{
if(line[0]=='#')
{
if(line == "#libs")
{
vec = &conf.libs;
loadFlag = FLAG_VECTOR;
}
else if(line == "#libdirs")
{
vec = &conf.libdirs;
loadFlag = FLAG_VECTOR;
}
else if(line == "#includedirs")
{
vec = &conf.includedirs;
loadFlag = FLAG_VECTOR;
}
else if(line == "#srcdirs")
{
vec = &conf.srcdirs;
loadFlag = FLAG_VECTOR;
}
else if(line == "#defines")
{
vec = &conf.defines;
loadFlag = FLAG_VECTOR;
}
else if(line == "#outputdir")
{
s = &conf.outputdir;
loadFlag = FLAG_STRING;
}
else if(line == "#outputname")
{
s = &conf.outputname;
loadFlag = FLAG_STRING;
}
else if(line == "#executable")
{
b = &conf.executable;
loadFlag = FLAG_BOOL;
}
else
{
LOG_ERROR("Invalid flag");
}
}
else
{
if(loadFlag == FLAG_STRING)
{
*s = line;
}
else if(loadFlag == FLAG_VECTOR)
{
vec->push_back(line);
}
else if(loadFlag == FLAG_BOOL)
{
if(line == "true")
*b = true;
else
*b = false;
}
}
}
return conf;
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <vector>
#include <string>
class ConfigFile
{
public:
std::vector<std::string> libs;
std::vector<std::string> libdirs;
std::vector<std::string> includedirs;
std::vector<std::string> srcdirs;
std::vector<std::string> defines;
std::string outputdir;
std::string outputname;
bool executable;
public:
ConfigFile();
void Save() const;
static ConfigFile Gen();
static ConfigFile Load();
};
+57
View File
@@ -0,0 +1,57 @@
#include "IncludeDeps.h"
std::set<std::string> IncludeDeps::printSet;
int IncludeDeps::printCounter = 0;
IncludeDeps::IncludeDeps(const std::string& filename, const std::string& dir, const std::map<std::string, std::string>& files, std::map<std::string, IncludeDeps*>& allDeps)
: filepath(dir+filename)
{
if(filename[filename.length() - 1] =='h')
{
allDeps.emplace(filepath, this);
}
std::ifstream file(filepath);
std::string line;
while(std::getline(file,line))
{
size_t pos = line.find("#include");
if(pos != std::string::npos)
{
std::string include = GetIncludeFile(line, pos, filename);
auto it = files.find(include);
if(it != files.end())
{
auto itD = allDeps.find(it->second + it->first);
if(itD == allDeps.end())
{
IncludeDeps* inc = new IncludeDeps(it->first, it->second,files,allDeps);
dependencies.emplace(it->second+ it->first, inc);
}else{
dependencies.emplace(itD->first, itD->second);
}
}
}
}
}
std::string IncludeDeps::GetIncludeFile(const std::string& line, size_t pos, const std::string& filename)
{
size_t bracket = line.find('<',pos);
if(bracket == std::string::npos)
{
bracket = line.find('\"',pos);
if(bracket == std::string::npos)
{
return "";
}
size_t slash = filename.find_last_of("/");
std::string include = line.substr(bracket+1, line.find('\"',bracket+1)-bracket-1);
if(slash == std::string::npos)
slash = -1;
return filename.substr(0,slash+1)+include;
}
else
{
return line.substr(bracket+1, line.find('>',bracket+1)-bracket-1);
}
}
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <fstream>
#include <exception>
struct CompareIncludeDeps;
class IncludeDeps
{
public:
std::map<std::string, IncludeDeps*> dependencies;
std::string filepath;
static std::set<std::string> printSet;
static int printCounter;
IncludeDeps(const std::string& filename, const std::string& dir, const std::map<std::string, std::string>& files, std::map<std::string, IncludeDeps*>& allDeps);
std::string GetIncludeFile(const std::string& line, size_t pos, const std::string& filename);
friend std::ostream& operator<<(std::ostream& stream, const IncludeDeps& deps)
{
if(printSet.find(deps.filepath) != printSet.end())
return stream;
printCounter++;
printSet.emplace(deps.filepath);
stream << deps.filepath;
for(auto it = deps.dependencies.begin();it!=deps.dependencies.end();++it)
{
stream << " " << *(it->second);
}
printCounter--;
if(printCounter == 0)
printSet.clear();
return stream;
}
IncludeDeps(const std::string& filename, const std::string& dir)
: filepath(dir+filename){}
};
struct CompareIncludeDeps
{
using is_transparent = void;
bool operator()(const IncludeDeps* d1, const IncludeDeps* d2) const
{
return d1->filepath < d2->filepath;
}
bool operator()(const IncludeDeps* d, const std::string& filepath) const
{
return d->filepath < filepath;
}
bool operator()(const std::string& filepath, const IncludeDeps* d) const
{
return filepath < d->filepath;
}
};
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <iostream>
#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
template <typename T>
void Log(const T& var)
{
std::cout << var;
}
template <typename T, typename ...Ts>
void Log(const T& var, const Ts& ...vars)
{
Log(var);
Log(vars...);
}
+135
View File
@@ -0,0 +1,135 @@
#include "Makefile.h"
#include <map>
#include <errno.h>
#include <dirent.h>
#include <cstring>
#include <fstream>
#include "Logging.h"
#include "IncludeDeps.h"
void Makefile::GetAllFiles(const std::string& folder, std::vector<std::string>& files)
{
DIR* dp;
struct dirent *dirp;
if((dp = opendir(folder.c_str())) == NULL){
LOG_ERROR(errno);
return;
}
while((dirp = readdir(dp)) != NULL)
{
if(dirp->d_type == DT_DIR)
{
if(strcmp(dirp->d_name,".") == 0)
continue;
if(strcmp(dirp->d_name,"..") == 0)
continue;
GetAllFiles(folder+dirp->d_name+"/", files);
}
else
{
files.push_back(folder+dirp->d_name);
}
}
closedir(dp);
}
void Makefile::Save(const ConfigFile& conf)
{
std::map<std::string, std::string> hFiles;
std::map<std::string, std::string> cppFiles;
PreSave(conf,hFiles,cppFiles);
std::ofstream outputFile("Makefile");
outputFile << "CC=@g++" << std::endl;
if(!conf.executable)
outputFile << "CO=@ar rs" << std::endl;
else
outputFile << "CO=@g++ -o" << std::endl;
outputFile << "BIN=" << conf.outputdir << std::endl;
outputFile << "OBJPATH=$(BIN)intermediates" << std::endl;
outputFile << "INCLUDES=";
for(auto it = conf.includedirs.begin();it!=conf.includedirs.end();++it)
{
outputFile << "-I./" << *it << " ";
}
outputFile << std::endl;
outputFile << "OBJECTS=";
for(auto it = cppFiles.begin();it!=cppFiles.end();++it)
{
size_t extensionPos = it->first.find_last_of(".");
size_t slash = it->first.find_last_of("/")+1;
outputFile << "$(OBJPATH)/" << it->first.substr(slash, extensionPos - slash) << ".o ";
}
outputFile << std::endl;
outputFile << "CFLAGS=$(INCLUDES) -std=c++17 -c -w -g3 ";
for(auto it = conf.defines.begin();it!=conf.defines.end();++it)
{
outputFile << "-D" << *it << " ";
}
outputFile << std::endl;
outputFile << "LIBS=";
for(auto it = conf.libs.begin();it!=conf.libs.end();++it)
{
outputFile << "-l:" << *it << " ";
}
outputFile << std::endl;
outputFile << "OUTPUT=$(BIN)" << conf.outputname << std::endl;
outputFile << "all: $(OUTPUT)" << std::endl;
outputFile << "\t$(info ------------------------)" << std::endl;
outputFile << "\t$(info ---- Done Compiling ----)" << std::endl;
outputFile << "\t$(info ------------------------)" << std::endl;
outputFile << "rebuid: clean all" << std::endl;
outputFile << "clean:" << std::endl;
outputFile << "\t$(info Removing intermediates)" << std::endl;
outputFile << "\trm -rf $(OBJPATH)/*.o" << std::endl;
outputFile << "$(OUTPUT): $(OBJECTS)" << std::endl;
outputFile << "\t$(info Generating output file)" << std::endl;
outputFile << "\t$(CO) $(OUTPUT) $(OBJECTS) $(LIBS)" << std::endl;
outputFile << "install: all" << std::endl;
outputFile << "\tcp $(OUTPUT) /usr/bin/" << conf.outputname << std::endl;
std::map<std::string, IncludeDeps*> dependencies;
for(auto it = cppFiles.begin(); it!=cppFiles.end();++it)
{
auto itD = dependencies.find(it->first+it->second);
if(itD == dependencies.end())
{
IncludeDeps* deps = new IncludeDeps(it->first, it->second,hFiles,dependencies);
size_t extensionPos = it->first.find_last_of(".");
size_t slash = it->first.find_last_of("/")+1;
std::string oFile = it->first.substr(slash, extensionPos - slash)+".o ";
outputFile << "$(OBJPATH)/" << oFile << ": " << *deps << std::endl;
outputFile << "\t$(info ---- $<)" << std::endl;
outputFile << "\t$(CC) $(CFLAGS) -o $@ $<" << std::endl;
//std::cout << *deps << std::endl;
}
}
}
void Makefile::PreSave(const ConfigFile& conf, std::map<std::string, std::string>& hFiles,
std::map<std::string, std::string>& cppFiles)
{
for(auto itSrc = conf.srcdirs.begin();itSrc != conf.srcdirs.end();++itSrc)
{
std::vector<std::string> files;
GetAllFiles(*itSrc,files);
// include paramenter with the path of the file
// For example src/graphics/Window.h -> graphics/Window.h if src is a src folder
for(auto it = files.begin(); it!=files.end();++it)
{
size_t extensionPos = it->find_last_of(".");
if(extensionPos != std::string::npos)
{
if(it->substr(extensionPos+1) == "cpp")
{
cppFiles.emplace(it->substr(itSrc->length()), *itSrc);
}
else
{
hFiles.emplace(it->substr(itSrc->length()), *itSrc);
}
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "ConfigFile.h"
#include <map>
class Makefile
{
public:
static void Save(const ConfigFile& conf);
private:
static void PreSave(const ConfigFile& conf, std::map<std::string, std::string>& hFiles, std::map<std::string, std::string>& cppFiles);
static void GetAllFiles(const std::string& folder, std::vector<std::string>& files);
};
+141
View File
@@ -0,0 +1,141 @@
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <map>
#include <fstream>
#include "IncludeDeps.h"
#include "ConfigFile.h"
#include "Makefile.h"
#include "Logging.h"
#define BIT(x) (1<<x)
const static unsigned int FLAG_HELP = BIT(0);
const static unsigned int FLAG_GEN= BIT(1);
// Flags for loading conf file
const static unsigned int LOAD_FLAG_ERROR= BIT(0);
const static unsigned int LOAD_FLAG_VECTOR = BIT(1);
const static unsigned int LOAD_FLAG_STRING = BIT(2);
int flags = 0;
void GenMakefile()
{
ConfigFile conf = ConfigFile::Load();
Makefile::Save(conf);
}
void ReadFlags(int argc, char** argv)
{
for(int i = 1;i<argc;i++)
{
if(strlen(argv[i]) > 1)
{
if(argv[i][0] == '-' && argv[i][1] == '-')
{
std::string flag(argv[i]);
if(flag == "--help")
{
flags |= FLAG_HELP;
}
else if(flag == "--gen")
{
flags |= FLAG_GEN;
}
}
}
}
}
void InputMultiple(const std::string& inputText, std::vector<std::string>& ret)
{
std::string input;
while(true)
{
LOG_INFO(inputText);
std::getline(std::cin, input);
if(input == "")
break;
ret.push_back(input);
}
}
void GenConfFile()
{
std::vector<std::string> libs;
std::vector<std::string> libdirs;
std::vector<std::string> includedirs;
std::vector<std::string> srcdirs;
std::string outputDir;
InputMultiple("Enter library:", libs);
InputMultiple("Enter library directory:", libdirs);
InputMultiple("Enter include directory:", includedirs);
InputMultiple("Enter source directories:", srcdirs);
LOG_INFO("Enter output directory (default: bin):");
std::getline(std::cin, outputDir);
if(outputDir == "")
outputDir = "bin";
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 << "#srcdirs" << std::endl;
for(auto it = srcdirs.begin();it!=srcdirs.end();++it)
{
file << *it << std::endl;
}
file << "#outputdir" << std::endl;
file << outputDir << std::endl;
file.close();
}
int main(int argc, char** argv)
{
ReadFlags(argc,argv);
if((flags & FLAG_HELP))
{
LOG_INFO("Usage: makegen [options]");
LOG_INFO(" Options:");
LOG_INFO(" -h, --help\tDisplays this information");
LOG_INFO(" -g, --gen\tGenerate a config file for the project");
return 0;
}
if(flags & FLAG_GEN)
{
GenConfFile();
return 0;
}
LOG_INFO("Generating Makefile...");
GenMakefile();
LOG_INFO("Running Makefile...");
for(int i = 1;i<argc;i++)
{
if(argv[i][0] != '-')
{
std::string make = std::string("make ") + argv[i];
system(make.c_str());
return 0;
}
}
system("make");
return 0;
}