#include #include #include #include #include #include #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 = ""; std::string compiler = "g++"; #ifdef _WIN32 char folder_char = '\\'; #else char folder_char = '/'; #endif std::string loaded_section = ""; std::vector exclude; std::vector keys = {"libs", "cppflags", "executable", "sourcepath", "buildpath", "exclude", "compiler"}; enum tokens {LIBS, CPPFLAGS, EXECUTABLE, SOURCEPATH, BUILDPATH, EXCLUDE, COMPILER}; bool must_link = false; bool must_recompile_all = false; std::mutex progress_mtx; struct FileInfo { std::string filename; }; 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}); cpp_index[name] = index; return index; } 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); if (active) { loaded_section = current_section; std::cout << " > " << current_section << " < " << std::endl; } else { std::cout << " - " << current_section << std::endl; } 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); else if (key == "compiler") compiler = 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 parse_dependency_file(const std::string& filename) { std::ifstream file(filename); if (!file) return {}; std::string merged; std::string line; while (std::getline(file, line)) { if (!line.empty() && line.back() == '\\') { line.pop_back(); merged += line; merged += ' '; } else { merged += line; merged += '\n'; } } auto colon = merged.find(':'); if (colon == std::string::npos) return {}; std::string deps = merged.substr(colon + 1); auto newline = deps.find('\n'); if (newline != std::string::npos) deps.resize(newline); std::vector result; std::stringstream ss(deps); std::string dep; while (ss >> dep) result.push_back(dep); return result; } bool DependenciesNeedRecompile( const std::string& object_file, const std::string& dependency_file) { if (!std::filesystem::exists(object_file)) return true; if (!std::filesystem::exists(dependency_file)) return true; auto object_time = std::filesystem::last_write_time(object_file); auto dependencies = parse_dependency_file( dependency_file); for (auto dep : dependencies) { try { dep = std::filesystem::weakly_canonical(dep).string(); } catch (...) { return true; // dependency disappeared } if (!std::filesystem::exists(dep)) return true; if (std::filesystem::last_write_time(dep) > object_time) return true; } return false; } bool MustRecompile(const FileInfo& file) { std::string object_file = generate_object_file_name(file.filename); std::string dependency_file = generate_dependency_file_name(file.filename); return DependenciesNeedRecompile( object_file, dependency_file); } void process_cpp(std::string& file) { std::string absolute_path = std::filesystem::weakly_canonical(file).string(); //std::cout << absolute_path << std::endl; add_cpp(absolute_path); } 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; } void parallel_compile(int thread_count) { std::queue work; std::mutex mtx; std::condition_variable cv; bool done = false; // Omplim la cua amb els arxius que necessiten recompilarse for (auto &file : cpp_files) { if (MustRecompile(file)) { work.push(file.filename); must_link = true; } } std::atomic completed = 0; int total = work.size(); progress_bar(0); auto worker = [&]() { while (true) { std::string job; // Extraure treball { std::unique_lock lock(mtx); cv.wait(lock, [&]() { return !work.empty() || done; }); if (work.empty()) return; // ja no hi ha res més que fer job = work.front(); work.pop(); } // Compilar Recompile(job); { std::lock_guard lock(progress_mtx); int done_now = ++completed; progress_bar(100 * done_now / total); } } }; // Llançar threads std::vector threads; for (int i = 0; i < thread_count; i++) threads.emplace_back(worker); // Notificar als threads { std::lock_guard lock(mtx); done = true; } cv.notify_all(); // Esperem a que acaben for (auto &t : threads) t.join(); progress_bar(100); printf("\n"); } int main(int argc, char *argv[]) { std::cout << " _ _ _ " << std::endl; std::cout << "| | __ _ __ _ _ _ ___(_)_ __| |_ ___ " << std::endl; std::cout << "| |/ _` |/ _` | | | |/ _ \\ | '__| __/ _ \\ " << std::endl; std::cout << "| | (_| | (_| | |_| | __/ | | | || (_) |" << std::endl; std::cout << "|_|\\__,_|\\__, |\\__,_|\\___|_|_| \\__\\___/ " << std::endl; std::cout << " |___/ 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); } std::string last_config_file = build_path + folder_char + "last_config"; if (!std::filesystem::exists(last_config_file)) { must_recompile_all = true; } else { std::ifstream in(last_config_file); if (in) { std::string word; in >> word; if (word != loaded_section) must_recompile_all = true; } } if (must_recompile_all) { std::cout << "Doing a full rebuild" << std::endl; std::filesystem::remove_all(build_path); } if (!std::filesystem::is_directory(build_path)) std::filesystem::create_directory(build_path); std::ofstream out(last_config_file); if (out) out << loaded_section; std::chrono::steady_clock::time_point begin_all = std::chrono::steady_clock::now(); 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 threads = std::thread::hardware_concurrency(); //printf("threads: %i\n", threads); if (threads == 0) threads = 4; // fallback parallel_compile(threads); // 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 = compiler + " " + build_path + folder_char + "*.o " + libs + " -o " + executable; //std::cout << command << std::endl; std::cout << "Linking..." << std::endl; fflush(stdout); int status = system(command.c_str()); if (status == -1) { std::cerr << "system() failed\n"; } else if (WIFEXITED(status)) { int code = WEXITSTATUS(status); std::cout << "exit code = " << code << '\n'; if (code != 0) { std::cerr << "ABORTED!\n"; exit(code); } } // int result = system(command.c_str()); // if (result != 0) { // std::cout << "ABORTED!" << std::endl; // exit(result); // } 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; } }