Files
lagueirto/main.cpp

546 lines
17 KiB
C++

#include <string>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <vector>
#include <unordered_map>
#include <chrono>
#include <algorithm>
#include <string.h>
#define VERSION "2.0.1"
std::string libs = "";
std::string cppflags = "";
std::string executable = "out";
std::string source_path = "";
std::vector<std::string> source_paths;
std::string build_path = "";
#ifdef _WIN32
std::string folder_char = "\\";
#else
std::string folder_char = "/";
#endif
std::vector<std::string> exclude;
std::vector<std::string> keys = {"libs", "cppflags", "executable", "sourcepath", "buildpath", "exclude"};
enum tokens {LIBS, CPPFLAGS, EXECUTABLE, SOURCEPATH, BUILDPATH, EXCLUDE};
bool must_link = false;
bool must_recompile_all = false;
struct FileInfo {
std::string filename;
std::filesystem::file_time_type time;
std::vector<int> included_headers;
};
std::vector<FileInfo> headers;
std::unordered_map<std::string, int> header_index;
bool has_header(const std::string& name) {
return header_index.find(name) != header_index.end();
}
int get_header_index(const std::string& name) {
auto it = header_index.find(name);
return (it != header_index.end()) ? it->second : -1;
}
int add_header(const std::string& name) {
auto it = header_index.find(name);
if (it != header_index.end())
return it->second;
int index = headers.size();
headers.push_back({name, std::filesystem::last_write_time(name), {}});
header_index[name] = index;
return index;
}
void add_include_to_header(int header_id, const std::string& header_name) {
int h = add_header(header_name);
headers[header_id].included_headers.push_back(h);
}
/*bool is_header_changed(const std::string& name) {
auto it = header_index.find(name);
if (it == header_index.end()) return false;
return headers[it->second].changed;
}*/
std::vector<FileInfo> cpp_files;
std::unordered_map<std::string, int> cpp_index;
bool has_cpp(const std::string& name) {
return cpp_index.find(name) != cpp_index.end();
}
int get_cpp_index(const std::string& name) {
auto it = cpp_index.find(name);
return (it != cpp_index.end()) ? it->second : -1;
}
int add_cpp(const std::string& name) {
auto it = cpp_index.find(name);
if (it != cpp_index.end())
return it->second;
int index = cpp_files.size();
cpp_files.push_back({name, std::filesystem::last_write_time(name), {}});
cpp_index[name] = index;
return index;
}
void add_include_to_cpp(int cpp_id, const std::string& header_name) {
int h = add_header(header_name);
cpp_files[cpp_id].included_headers.push_back(h);
}
bool contains(const std::vector<std::string>& v, const std::string& s) { return std::find(v.begin(), v.end(), s) != v.end(); }
std::vector<std::string> split(std::string str)
{
std::vector<std::string> strings;
char tmp[100];
int tmp_p = 0, str_p = 0;
while (str[str_p]!=0)
{
if (str[str_p]!=32)
tmp[tmp_p++] = str[str_p++];
else
{
tmp[tmp_p]=0;
strings.push_back(tmp);
tmp_p=0; while (str[str_p]==32) str_p++;
}
}
tmp[tmp_p]=0;
strings.push_back(tmp);
return strings;
}
char *getBufferFromFile(const char* filename)
{
FILE *f = fopen(filename, "rb");
if (!f) {
perror("Error opening file");
exit(-1);
}
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
char *buffer = (char*)malloc(size+1);
fread(buffer, size, 1, f);
buffer[size] = 0;
fclose(f);
return buffer;
}
static inline void trim(std::string &s) {
while (!s.empty() && isspace(s.front())) s.erase(s.begin());
while (!s.empty() && isspace(s.back())) s.pop_back();
}
static inline std::string trim_copy(std::string s) {
trim(s);
return s;
}
bool loadLagueirtoFile(const std::string &section_to_load)
{
std::ifstream f("lagueirtofile");
if (!f) { std::cerr << "Cannot open lagueirtofile\n"; exit(1); }
std::string line;
std::string current_section {"@none@"};
bool found_any_section = false;
bool found_default = false;
bool active = false;
while (std::getline(f, line))
{
// Quitar espacios al inicio y final
trim(line);
if (line.empty() || line[0] == '#')
continue;
// Detectar sección
if (line.front() == '[')
{
auto end = line.find(']');
if (end == std::string::npos) continue;
current_section = line.substr(1, end - 1);
// Detectar si es default
std::string rest = trim_copy(line.substr(end + 1));
found_any_section = true;
if (rest == "default") found_default = true;
if (section_to_load.empty() && rest == "default") active = true;
else active = (current_section == section_to_load);
continue;
}
// Clave = valor
auto pos = line.find('=');
if (pos == std::string::npos)
continue;
std::string key = trim_copy(line.substr(0, pos));
std::string value = trim_copy(line.substr(pos + 1));
if (!active && current_section != "@none@") continue;
if (key == "libs") libs = value;
else if (key == "cppflags") cppflags = value;
else if (key == "executable") executable = value;
else if (key == "sourcepath") source_path = value;
else if (key == "buildpath") build_path = value;
else if (key == "exclude") exclude = split(value);
}
return !(found_any_section && section_to_load.empty() && !found_default);
}
std::string getFileExtension(std::string path)
{
std::size_t dotpos = path.find_last_of(".");
return dotpos <= 0 ? "" : path.substr(dotpos + 1);
}
std::string getFileNameWithoutExtension(std::string path)
{
std::size_t slashpos = path.find_last_of(folder_char);
std::string filename = path.substr(slashpos+1);
std::size_t dotpos = filename.find_last_of(".");
if (dotpos <= 0) return filename;
return filename.substr(0, dotpos);
}
const bool textFound(char *buffer, const char *text)
{
const int strsize = strlen(text);
int i = 0;
bool equal=true;
while (equal && i<strsize)
{
if (buffer[i]!=text[i]) equal = false;
++i;
}
return equal;
}
std::vector<std::string> getIncludes(const std::string &filename) {
std::ifstream f(filename);
if (!f) return {};
std::vector<std::string> includes;
std::string line;
bool inBlockComment = false;
while (std::getline(f, line)) {
std::string processed;
processed.reserve(line.size());
for (size_t i = 0; i < line.size(); ++i) {
// Detectar inicio de comentario de bloque
if (!inBlockComment && i + 1 < line.size() && line[i] == '/' && line[i+1] == '*') {
inBlockComment = true;
i++; // saltar '*'
continue;
}
// Detectar fin de comentario de bloque
if (inBlockComment && i + 1 < line.size() && line[i] == '*' && line[i+1] == '/') {
inBlockComment = false;
i++; // saltar '/'
continue;
}
// Si estamos dentro de un bloque, ignorar todo
if (inBlockComment)
continue;
// Detectar comentario de línea
if (i + 1 < line.size() && line[i] == '/' && line[i+1] == '/') {
break; // ignorar el resto de la línea
}
processed.push_back(line[i]);
}
// Si la línea quedó vacía tras limpiar comentarios, saltar
if (processed.find("#include") == std::string::npos)
continue;
// Buscar #include
size_t pos = processed.find("#include");
pos += 8;
// Saltar espacios
while (pos < processed.size() && (processed[pos] == ' ' || processed[pos] == '\t'))
pos++;
// Solo includes con comillas
if (pos >= processed.size() || processed[pos] != '"')
continue;
pos++; // saltar comilla inicial
size_t start = pos;
// Buscar comilla final
while (pos < processed.size() && processed[pos] != '"')
pos++;
if (pos < processed.size())
includes.push_back(processed.substr(start, pos - start));
}
return includes;
}
bool HeadersNeedRecompile(FileInfo file, std::filesystem::file_time_type object_file_write_time) {
for (auto include : file.included_headers) {
auto include_file_write_time = headers[include].time;
if (include_file_write_time > object_file_write_time) {
return true;
} else {
if (HeadersNeedRecompile(headers[include], object_file_write_time)) return true;
}
}
return false;
}
bool MustRecompile(FileInfo file) {
std::string object_file = build_path + folder_char + std::filesystem::path(file.filename).stem().string() + ".o";
// si el objecte no existeix, fa falta recompilar
if (!std::filesystem::exists(object_file)) {
return true;
} else {
// Si sí que existeix, agafem la data de modificació
auto object_file_write_time = std::filesystem::last_write_time(object_file);
// Si la data de modificació del cpp es major que la del objecte, fa falta recompilar
auto source_file_write_time = file.time;
if (source_file_write_time > object_file_write_time) {
return true;
} else {
if (HeadersNeedRecompile(file, object_file_write_time)) return true;
}
}
return false;
}
void Recompile(std::string source_file) {
std::string object_file = build_path + folder_char + std::filesystem::path(source_file).stem().string() + ".o";
must_link = true;
std::string command = "g++ " + source_file + " " + cppflags + " -c -o " + object_file;
//std::cout << command << std::endl;
if (system(command.c_str()) != 0) {
std::cout << "Compilation failed! Aborting..." << std::endl;
exit(1);
}
}
void process_includes(int index, std::string& file, bool is_cpp = false)
{
// Trobem els includes en l'arxiu
auto include_files = getIncludes(file);
// Per a cada include...
for (auto& include : include_files) {
// montem la ruta al include a partir de la del arxiu que l'inclou
std::string include_file = std::filesystem::path(file).remove_filename().string() + include;
// Comprobem si existeix relativament a l'arxiu que l'inclou
bool found = std::filesystem::exists(include_file);
if (!found) {
// Si no, comprobem si existeix relativament a cada un dels source paths especificats
for (auto path : source_paths) {
include_file = path + "/" + include;
if (std::filesystem::exists(include_file)) {
found = true;
break;
}
}
}
// Si existeix l'incloguem
if (found) {
auto header_absolute_path = std::filesystem::weakly_canonical(include_file).string();
const bool already_exists = (has_header(header_absolute_path));
if (is_cpp)
add_include_to_cpp(index, header_absolute_path);
else
add_include_to_header(index, header_absolute_path);
if (!already_exists) process_includes(get_header_index(header_absolute_path), header_absolute_path);
} else {
std::cout << "WARNING: file '" << include <<"' not found" << std::endl;
}
}
}
void process_cpp(std::string& file)
{
std::string absolute_path = std::filesystem::weakly_canonical(file).string();
//std::cout << absolute_path << std::endl;
int index = add_cpp(absolute_path);
process_includes(index, absolute_path, true);
}
void print_header_tree(int indent, const std::vector<int>& files)
{
for (auto index : files) {
std::cout << std::string(indent*2, ' ') << headers[index].filename << std::endl;
auto &include_list = headers[index].included_headers;
if (!include_list.empty()) print_header_tree(indent+1, include_list);
}
}
void print_file_tree(int indent, const std::vector<FileInfo>& files)
{
for (auto& file : files) {
std::cout << std::string(indent*2, ' ') << file.filename << std::endl;
auto &include_list = file.included_headers;
if (!include_list.empty()) {
print_header_tree(indent+1, include_list);
} else {
std::cout << std::string((indent+1)*2, ' ') << "<no headers>" << std::endl;
}
}
}
void progress_bar(int percent) {
const int width = 50; // ancho de la barra
int filled = (percent * width) / 100;
printf("\r[");
for (int i = 0; i < width; i++) {
if (i < filled) printf("#");
else printf(" ");
}
printf("] %3d%%", percent);
fflush(stdout);
}
void processCommand(std::string arg) {
std::cout << "command: '" << arg << "'" << std::endl;
if (arg == "-r") must_recompile_all = true;
}
int main(int argc, char *argv[])
{
std::cout << "Lagueirto v" << VERSION << std::endl;
std::string configuration_to_use;
for (int i = 1; i < argc; ++i)
{
std::string arg = argv[i];
if (!arg.empty() && arg[0] == '-') processCommand(arg);
else configuration_to_use = arg;
}
if (!loadLagueirtoFile(configuration_to_use)) {
std::cerr << "No default section found.\n";
exit(1);
}
if (must_recompile_all) {
std::cout << "remove: '" << build_path << "'" << std::endl;
std::filesystem::remove_all(build_path);
}
std::chrono::steady_clock::time_point begin_all = std::chrono::steady_clock::now();
if (!std::filesystem::is_directory(build_path)) std::filesystem::create_directory(build_path);
source_paths = split(source_path);
// Recopilem tots els arxius cpp i capçaleres
// ===================================================================
for (auto &src_path : source_paths) {
bool recursive = false;
if (!src_path.empty() && src_path.back() == '+') {
recursive = true;
src_path.pop_back();
}
#ifdef _WIN32
std::replace(src_path.begin(), src_path.end(), '/', '\\');
#endif
if (!std::filesystem::is_directory(src_path)) {
if (std::filesystem::is_regular_file(src_path)) {
std::string ext = getFileExtension(src_path);
if (ext == "cpp" || ext == "c") {
process_cpp(src_path);
//MaybeRecompile(src_path);
} else {
std::cout << "ERROR: '" << src_path << "' is not a .c/.cpp file." << std::endl;
exit(1);
}
} else {
std::cout << "ERROR: '" << src_path << "' does not exist." << std::endl;
exit(1);
}
} else {
std::string path = "." + folder_char + src_path;
if (recursive) {
for (const auto &entry : std::filesystem::recursive_directory_iterator(path)) {
if (!entry.is_regular_file()) continue;
std::string source_file = entry.path().string();
std::string ext = getFileExtension(source_file);
if ((ext == "cpp" || ext == "c") && !contains(exclude, entry.path().filename().string())) {
process_cpp(source_file);
//MaybeRecompile(source_file);
}
}
} else {
for (const auto &entry : std::filesystem::directory_iterator(path)) {
if (!entry.is_regular_file()) continue;
std::string source_file = entry.path().string();
std::string ext = getFileExtension(source_file);
if ((ext == "cpp" || ext == "c") && !contains(exclude, entry.path().filename().string())) {
process_cpp(source_file);
//MaybeRecompile(source_file);
}
}
}
}
}
//print_file_tree(0, cpp_files);
int i = 0;
int total = cpp_files.size();
progress_bar(0);
for (auto& file : cpp_files) {
if (MustRecompile(file)) Recompile(file.filename);
progress_bar(100*float(float(i)/float(total)));
i++;
}
progress_bar(100);
std::cout << std::endl;
if (must_link) {
std::string command = "g++ " + build_path + folder_char + "*.o " + libs + " -o " + executable;
//std::cout << command << std::endl;
std::cout << "Linking..."; fflush(stdout);
if (system(command.c_str()) != 0) {
std::cout << "ABORTED!" << std::endl;
exit(1);
}
std::cout << "DONE!" << std::endl;
std::chrono::steady_clock::time_point end_all = std::chrono::steady_clock::now();
float t = float(std::chrono::duration_cast<std::chrono::microseconds>(end_all - begin_all).count())/1000000;
std::cout << "(" << t << " seconds)" << std::endl;
} else {
std::cout << "Everything is up to date. Nothing to do." << std::endl;
}
}