Files
lagueirto/main.cpp
T
Raimon @ quifisraimon 5a0c4a0ba7 VERSIÓ 2.2.0:
- [NEW] Nova forma de parejar includes, gentileza del propi gcc
2026-09-21 13:53:51 +02:00

559 lines
16 KiB
C++

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <vector>
#include <unordered_map>
#include <chrono>
#include <algorithm>
#include <string.h>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <atomic>
#include "version.h"
std::string libs = "";
std::string cppflags = "";
std::string executable = "out";
std::string source_path = "";
std::vector<std::string> 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<std::string> exclude;
std::vector<std::string> 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<FileInfo> cpp_files;
std::unordered_map<std::string, int> 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<std::string>& v, const std::string& s) { return std::find(v.begin(), v.end(), s) != v.end(); }
std::vector<std::string> split(std::string str)
{
std::vector<std::string> 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 &section_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<strsize)
{
if (buffer[i]!=text[i]) equal = false;
++i;
}
return equal;
}
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);
std::string out = std::filesystem::relative(target, cwd).replace_extension("o").generic_string();
for (char& c : out) if (c == '/') c = '.';
return build_path + folder_char + out;
}
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 + " -MMD -MP -c -o " + object_file;
//std::cout << command << std::endl;
if (system(command.c_str()) != 0) {
std::cout << "Compilation failed! Aborting..." << std::endl;
exit(1);
}
}
std::string generate_dependency_file_name(
const std::string& source_file)
{
std::string name = generate_object_file_name(source_file);
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 {
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;
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<std::string> 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<int> completed = 0;
int total = work.size();
progress_bar(0);
auto worker = [&]() {
while (true) {
std::string job;
// Extraure treball
{
std::unique_lock<std::mutex> 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<std::mutex> lock(progress_mtx);
int done_now = ++completed;
progress_bar(100 * done_now / total);
}
}
};
// Llançar threads
std::vector<std::thread> threads;
for (int i = 0; i < thread_count; i++)
threads.emplace_back(worker);
// Notificar als threads
{
std::lock_guard<std::mutex> 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<std::chrono::microseconds>(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;
}
}