#include #include #include #include #include #include #include #include #include #include "version.h" std::string libs = ""; std::string cppflags = ""; std::string executable = "out"; std::string source_path = ""; std::vector source_paths; std::string build_path = ""; #ifdef _WIN32 std::string folder_char = "\\"; #else std::string folder_char = "/"; #endif std::vector exclude; std::vector 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 included_headers; }; std::vector headers; std::unordered_map 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 cpp_files; std::unordered_map 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& v, const std::string& s) { return std::find(v.begin(), v.end(), s) != v.end(); } std::vector split(std::string str) { std::vector 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 §ion_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 getIncludes(const std::string &filename) { std::ifstream f(filename); if (!f) return {}; std::vector 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& 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& 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, ' ') << "" << 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" << LAGUEIRTO_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(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; } }