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 <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -40,38 +41,7 @@ std::mutex progress_mtx;
|
|||||||
|
|
||||||
struct FileInfo {
|
struct FileInfo {
|
||||||
std::string filename;
|
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::vector<FileInfo> cpp_files;
|
||||||
std::unordered_map<std::string, int> cpp_index;
|
std::unordered_map<std::string, int> cpp_index;
|
||||||
@@ -89,15 +59,10 @@ int add_cpp(const std::string& name) {
|
|||||||
return it->second;
|
return it->second;
|
||||||
|
|
||||||
int index = cpp_files.size();
|
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;
|
cpp_index[name] = index;
|
||||||
return 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(); }
|
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;
|
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::string generate_object_file_name(std::string filename) {
|
||||||
std::filesystem::path cwd = std::filesystem::weakly_canonical(std::filesystem::current_path());
|
std::filesystem::path cwd = std::filesystem::weakly_canonical(std::filesystem::current_path());
|
||||||
std::filesystem::path target = std::filesystem::weakly_canonical(filename);
|
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;
|
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) {
|
void Recompile(std::string source_file) {
|
||||||
std::string object_file = generate_object_file_name(source_file);
|
std::string object_file = generate_object_file_name(source_file);
|
||||||
must_link = true;
|
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;
|
//std::cout << command << std::endl;
|
||||||
|
|
||||||
if (system(command.c_str()) != 0) {
|
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
|
std::string name = generate_object_file_name(source_file);
|
||||||
auto include_files = getIncludes(file);
|
|
||||||
|
|
||||||
// Per a cada include...
|
name.replace(name.size() - 1, 1, "d");
|
||||||
for (auto& include : include_files) {
|
|
||||||
// montem la ruta al include a partir de la del arxiu que l'inclou
|
return name;
|
||||||
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);
|
std::vector<std::string> parse_dependency_file(const std::string& filename)
|
||||||
if (!found) {
|
{
|
||||||
// Si no, comprobem si existeix relativament a cada un dels source paths especificats
|
std::ifstream file(filename);
|
||||||
for (auto path : source_paths) {
|
|
||||||
include_file = path + "/" + include;
|
if (!file) return {};
|
||||||
if (std::filesystem::exists(include_file)) {
|
|
||||||
found = true;
|
std::string merged;
|
||||||
break;
|
std::string line;
|
||||||
}
|
|
||||||
}
|
while (std::getline(file, line))
|
||||||
}
|
{
|
||||||
// Si existeix l'incloguem
|
if (!line.empty() && line.back() == '\\') {
|
||||||
if (found) {
|
line.pop_back();
|
||||||
auto header_absolute_path = std::filesystem::weakly_canonical(include_file).string();
|
merged += line;
|
||||||
const bool already_exists = (has_header(header_absolute_path));
|
merged += ' ';
|
||||||
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 {
|
} 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)
|
void process_cpp(std::string& file)
|
||||||
{
|
{
|
||||||
std::string absolute_path = std::filesystem::weakly_canonical(file).string();
|
std::string absolute_path = std::filesystem::weakly_canonical(file).string();
|
||||||
//std::cout << absolute_path << std::endl;
|
//std::cout << absolute_path << std::endl;
|
||||||
int index = add_cpp(absolute_path);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void progress_bar(int percent) {
|
void progress_bar(int percent) {
|
||||||
@@ -644,11 +525,30 @@ std::cout << " |___/ v" << LAGUEIRTO_VERSION << std::endl;
|
|||||||
if (must_link) {
|
if (must_link) {
|
||||||
std::string command = compiler + " " + build_path + folder_char + "*.o " + libs + " -o " + executable;
|
std::string command = compiler + " " + build_path + folder_char + "*.o " + libs + " -o " + executable;
|
||||||
//std::cout << command << std::endl;
|
//std::cout << command << std::endl;
|
||||||
std::cout << "Linking..."; fflush(stdout);
|
std::cout << "Linking..." << std::endl; fflush(stdout);
|
||||||
if (system(command.c_str()) != 0) {
|
|
||||||
std::cout << "ABORTED!" << std::endl;
|
int status = system(command.c_str());
|
||||||
exit(1);
|
|
||||||
|
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::cout << "DONE!" << std::endl;
|
||||||
std::chrono::steady_clock::time_point end_all = std::chrono::steady_clock::now();
|
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;
|
float t = float(std::chrono::duration_cast<std::chrono::microseconds>(end_all - begin_all).count())/1000000;
|
||||||
|
|||||||
Reference in New Issue
Block a user