VERSIÓ 2.2.0:
- [NEW] Nova forma de parejar includes, gentileza del propi gcc
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user