#include #include #include #include #include #include #include #include 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 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(std::string file, std::filesystem::file_time_type object_file_write_time) { auto include_files = getIncludes(file); for (auto include : include_files) { std::filesystem::path fullpath(file); auto path_without_filename = fullpath.remove_filename(); std::string src_path = path_without_filename.string(); std::string include_file = src_path + include; bool found = std::filesystem::exists(include_file); if (!found) { for (auto path : source_paths) { include_file = path + "/" + include; if (std::filesystem::exists(include_file)) { found = true; break; } } } if (!found) { std::cout << "WARNING: Include file '" << include << "' not found in '" << file << "'." << std::endl; } else { auto include_file_write_time = std::filesystem::last_write_time(include_file); if (include_file_write_time > object_file_write_time) { return true; } else { if (HeadersNeedRecompile(include_file, object_file_write_time)) return true; } } } return false; } bool MustRecompile(std::string source_file) { std::string object_file = build_path + folder_char + getFileNameWithoutExtension(source_file)+".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 = std::filesystem::last_write_time(source_file); if (source_file_write_time > object_file_write_time) { return true; } else { if (HeadersNeedRecompile(source_file, object_file_write_time)) return true; } } return false; } void MaybeRecompile(std::string source_file) { if (MustRecompile(source_file)) { std::string object_file = build_path + folder_char + getFileNameWithoutExtension(source_file)+".o"; must_link = true; std::string command = "g++ " + source_file + " " + cppflags + " -c -o " + object_file; std::cout << command << std::endl; std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); int result = system(command.c_str()); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); float t = float(std::chrono::duration_cast(end - begin).count())/1000000; std::cout << "(" << t << " seconds)" << std::endl; if (result != 0) { std::cout << "Compilation failed! Aborting..." << std::endl; exit(1); } } else { //std::cout << object_file << " està actualitzat" << std::endl; } } void processCommand(std::string arg) { // Do nothing for now } int main(int argc, char *argv[]) { 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); } 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); 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") { 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())) { 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())) { MaybeRecompile(source_file); } } } } } if (must_link) { std::string command = "g++ " + build_path + folder_char + "*.o " + libs + " -o " + executable; std::cout << command << std::endl; if (system(command.c_str()) != 0) { std::cout << "ABORTED!" << std::endl; exit(1); } 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; } }