VERSIÓ 2.2.0:

- [NEW] Nova forma de parejar includes, gentileza del propi gcc
This commit is contained in:
Raimon @ quifisraimon
2026-09-21 13:53:51 +02:00
parent ae8f476c84
commit 5a0c4a0ba7
2 changed files with 98 additions and 198 deletions
+97 -197
View File
@@ -1,6 +1,7 @@
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <vector>
#include <unordered_map>
@@ -40,38 +41,7 @@ std::mutex progress_mtx;
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;
@@ -89,15 +59,10 @@ int add_cpp(const std::string& name) {
return it->second;
int index = cpp_files.size();
cpp_files.push_back({name, std::filesystem::last_write_time(name), {}});
cpp_files.push_back({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(); }
@@ -243,87 +208,6 @@ const bool textFound(char *buffer, const char *text)
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;
}
std::string generate_object_file_name(std::string filename) {
std::filesystem::path cwd = std::filesystem::weakly_canonical(std::filesystem::current_path());
std::filesystem::path target = std::filesystem::weakly_canonical(filename);
@@ -332,31 +216,11 @@ std::string generate_object_file_name(std::string filename) {
return build_path + folder_char + out;
}
bool MustRecompile(FileInfo file) {
std::string object_file = generate_object_file_name(file.filename);
// 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 = generate_object_file_name(source_file);
must_link = true;
std::string command = compiler + " " + source_file + " " + cppflags + " -c -o " + object_file;
std::string command = compiler + " " + source_file + " " + cppflags + " -MMD -MP -c -o " + object_file;
//std::cout << command << std::endl;
if (system(command.c_str()) != 0) {
@@ -365,70 +229,87 @@ void Recompile(std::string source_file) {
}
}
void process_includes(int index, std::string& file, bool is_cpp = false)
std::string generate_dependency_file_name(
const std::string& source_file)
{
// Trobem els includes en l'arxiu
auto include_files = getIncludes(file);
std::string name = generate_object_file_name(source_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);
name.replace(name.size() - 1, 1, "d");
return name;
}
std::vector<std::string> 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 {
std::cout << "WARNING: file '" << include <<"' not found" << std::endl;
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<std::string> 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;
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;
}
}
add_cpp(absolute_path);
}
void progress_bar(int percent) {
@@ -644,11 +525,30 @@ std::cout << " |___/ v" << LAGUEIRTO_VERSION << 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..."; fflush(stdout);
if (system(command.c_str()) != 0) {
std::cout << "ABORTED!" << std::endl;
exit(1);
}
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<std::chrono::microseconds>(end_all - begin_all).count())/1000000;