diff --git a/Crafter.Build-ModuleFile.cpp b/Crafter.Build-ModuleFile.cpp index 2e19190..bc0b4e6 100644 --- a/Crafter.Build-ModuleFile.cpp +++ b/Crafter.Build-ModuleFile.cpp @@ -25,108 +25,194 @@ module; #include #include #include +#include module Crafter.Build; using namespace Crafter::Build; namespace fs = std::filesystem; - -ModuleFile* ModuleFile::CompileModuleFile(fs::path path, std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target) { - if(fs::exists(path.generic_string()+".cppm")) { - allFilesMutex.lock(); - if(!allFiles.contains(path)) { - return new ModuleFile(path, clangDir, config, pcmDir, target); - } else{ - allFilesMutex.unlock(); - return allFiles[path]; - } - } else{ - return nullptr; - } -} - -ModuleFile::ModuleFile(fs::path path, std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target): path(path), recompiled(false) { - fileMutex.lock(); - allFiles.insert({path,this}); - allFilesMutex.unlock(); - needsRecompiling = !fs::exists((pcmDir/path.filename()).generic_string()+".pcm") || fs::last_write_time(path.generic_string()+".cppm") > fs::last_write_time((pcmDir/path.filename()).generic_string()+".pcm"); - std::ifstream t(path.generic_string()+".cppm"); - std::stringstream buffer; - buffer << t.rdbuf(); - std::string fileContent = buffer.str(); - - std::regex rgx("import (.*);"); - std::smatch matches; - std::regex_search(fileContent, matches, rgx); - - std::string::const_iterator searchStart( fileContent.cbegin() ); - while ( regex_search( searchStart, fileContent.cend(), matches, rgx ) ) +ModulePartition::ModulePartition(const std::string& name, const fs::path& path, Module* parent, const std::string& fileContent, const fs::path& pcmDir) : name(name), path(path), parent(parent) { { - std::string matchString = matches[1].str(); - if(matchString[0] == ':'){ - std::regex rgx2("export module ([a-zA-Z_\\-0-9\\.]*).*;"); - std::smatch matches2; - std::regex_search(fileContent, matches2, rgx2); - matchString.erase(0, 1); - matchString = matches2[1].str()+"-"+matchString; + std::regex pattern(R"(import :([a-zA-Z_\-0-9\.]*);)"); + std::sregex_iterator currentMatch(fileContent.begin(), fileContent.end(), pattern); + std::sregex_iterator lastMatch; + + while (currentMatch != lastMatch) { + std::smatch match = *currentMatch; + partitionDependencies.push_back(match[1]); + ++currentMatch; } - fs::path importPath = path.remove_filename()/fs::path(matchString); - ModuleFile* file = CompileModuleFile(importPath,clangDir, config, pcmDir, target); - if(file){ - file->fileMutex.lock(); - - if(file->needsRecompiling){ - needsRecompiling = true; - } - - file->fileMutex.unlock(); - dependencies.push_back(file); - } - searchStart = matches.suffix().first; } - fileMutex.unlock(); - Compile(clangDir, config, pcmDir, target); + { + std::regex pattern(R"(import ([a-zA-Z_\-0-9\.]*);)"); + std::sregex_iterator currentMatch(fileContent.begin(), fileContent.end(), pattern); + std::sregex_iterator lastMatch; + + while (currentMatch != lastMatch) { + std::smatch match = *currentMatch; + moduleDependencies.push_back(match[1]); + ++currentMatch; + } + } + if(!fs::exists((pcmDir/path.filename()).generic_string()+".pcm")) { + needsRecompiling = true; + needsRecompilingDependency = true; + } else if(fs::last_write_time(path.generic_string()+".cppm") > fs::last_write_time((pcmDir/path.filename()).generic_string()+".pcm")) { + needsRecompiling = true; + needsRecompilingDependency = true; + } } -void ModuleFile::Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target) { - if(needsRecompiling) { - fileMutex.lock(); - if(!recompiled){ - for(ModuleFile* dep : dependencies){ - dep->Compile(clangDir, config, pcmDir, target); - } - - std::string flags; - for(const std::string& flag : config.flags) { - flags+=flag; - } - - if(config.debug) { - flags+=" -g"; - } - - std::string march; - if(config.target != "wasm32-unknown-wasi"){ - march = std::format("-march={}", config.march); - } - - std::string command = std::format("{} {} -std={} {}.cppm --precompile {} -fprebuilt-module-path={} -o {}.pcm {}", clangDir, flags, config.standard, path.generic_string(), march, pcmDir.generic_string(), (pcmDir/path.filename()).generic_string(), target); - if(config.verbose) { - std::cout << command << std::endl; - } - system(command.c_str()); - - - - recompiled = true; - fileMutex.unlock(); - - command = std::format("{} -std={} {}.pcm -fprebuilt-module-path={} -c -O{} {} {} -o {}.o {}", clangDir, config.standard, (pcmDir/path.filename()).generic_string(), pcmDir.generic_string(), config.optimizationLevel, march, flags, (config.buildDir/path.filename()).generic_string(), target); - if(config.verbose) { - std::cout << command << std::endl; - } - system(command.c_str()); +void ModulePartition::AddDependants() { + for(const std::string& dependency: partitionDependencies) { + auto pos = parent->partitions.find(dependency); + if (pos != parent->partitions.end()) { + pos->second.partitionDependants.push_back(this); + partitionDependenciesP.push_back(&pos->second); } else { - fileMutex.unlock(); + throw std::runtime_error(std::format("Partition {}:{} not found", parent->name, dependency)); } } } + +void ModulePartition::Check() { + if(!needsRecompiling) { + for(ModulePartition* dependency : partitionDependenciesP) { + if(dependency->needsRecompilingDependency) { + needsRecompiling = true; + break; + } + } + } +} + +void ModulePartition::Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags) { + if(needsRecompiling) { + for(ModulePartition* dependency : partitionDependenciesP) { + if(dependency->needsRecompiling) { + dependency->compiled->wait(false); + } + } + std::string command = std::format("{} {} -std={} {}.cppm --precompile {} -fprebuilt-module-path={} -o {}.pcm {}", clangDir, flags, config.standard, path.generic_string(), march, pcmDir.generic_string(), (pcmDir/path.filename()).generic_string(), target); + if(config.verbose) { + std::cout << command << std::endl; + } + system(command.c_str()); + } + *compiled = true; + compiled->notify_all(); +} +void ModulePartition::CompileSource(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, const fs::path& buildDir) { + std::string command = std::format("{} -std={} {}.pcm -fprebuilt-module-path={} -c -O{} {} {} -o {}.o {}", clangDir, config.standard, (pcmDir/path.filename()).generic_string(), pcmDir.generic_string(), config.optimizationLevel, march, flags, (buildDir/path.filename()).generic_string(), target); + if(config.verbose) { + std::cout << command << std::endl; + } + system(command.c_str()); +} + +Module::Module(const std::string& name, const fs::path& path, const Configuration& config, const fs::path& pcmDir, std::string& files, const fs::path& buildDir) : name(name), path(path) { + for(const fs::path& file: config.moduleFiles) { + std::ifstream t(file.generic_string()+".cppm"); + std::stringstream buffer; + buffer << t.rdbuf(); + std::string fileContent = buffer.str(); + + std::regex pattern("export module ([a-zA-Z_\\-0-9\\.]*):([a-zA-Z_\\-0-9\\.]*);"); + std::smatch match; + if (std::regex_search(fileContent, match, pattern)) { + if(match[1] == name) { + partitions.insert({match[2], ModulePartition(match[2], file, this, fileContent, pcmDir)}); + files += std::format("{}.o ",(buildDir/file.filename()).generic_string()); + } + } + } + + if(!fs::exists((pcmDir/path.filename()).generic_string()+".pcm")) { + needsRecompiling = true; + } else if(fs::last_write_time(path.generic_string()+".cppm") > fs::last_write_time((pcmDir/path.filename()).generic_string()+".pcm")) { + needsRecompiling = true; + } + + + for(auto& [key, val] : partitions) { + val.AddDependants(); + } + + files += std::format("{}.o ",(buildDir/path.filename()).generic_string()); +} + +void Module::Check() { + for(auto& [key, val] : partitions) { + val.Check(); + } + + for(auto& [key, val] : partitions) {; + if(val.needsRecompilingDependency) { + needsRecompiling = true; + break; + } + } +} + +void Module::Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, const fs::path& buildDir) { + std::vector threads; + for(auto& [key, val] : partitions) {; + if(val.needsRecompiling) { + threads.emplace_back([&val, &clangDir, &config, &pcmDir, &target, &march, &flags](){ + val.Compile(clangDir, config, pcmDir, target, march, flags); + }); + } + } + + for(std::thread& thread : threads){ + thread.join(); + } + + { + std::string command = std::format("{} {} -std={} {}.cppm --precompile {} -fprebuilt-module-path={} -o {}.pcm {}", clangDir, flags, config.standard, path.generic_string(), march, pcmDir.generic_string(), (pcmDir/path.filename()).generic_string(), target); + if(config.verbose) { + std::cout << command << std::endl; + } + system(command.c_str()); + } + + std::vector threads2; + for(auto& [key, val] : partitions) {; + if(val.needsRecompiling) { + threads2.emplace_back([&val, &clangDir, &config, &pcmDir, &target, &march, &flags, &buildDir](){ + val.CompileSource(clangDir, config, pcmDir, target, march, flags, buildDir); + }); + } + } + threads2.emplace_back([this, &clangDir, &config, &pcmDir, &target, &march, &flags, &buildDir](){ + std::string command = std::format("{} -std={} {}.pcm -fprebuilt-module-path={} -c -O{} {} {} -o {}.o {}", clangDir, config.standard, (pcmDir/path.filename()).generic_string(), pcmDir.generic_string(), config.optimizationLevel, march, flags, (buildDir/path.filename()).generic_string(), target); + if(config.verbose) { + std::cout << command << std::endl; + } + system(command.c_str()); + }); + + for(std::thread& thread : threads2){ + thread.join(); + } +} + +std::vector Module::GetModules(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, std::string& files, const fs::path& buildDir) { + std::vector modules; + for(const fs::path& file: config.moduleFiles) { + std::ifstream t(file.generic_string()+".cppm"); + std::stringstream buffer; + buffer << t.rdbuf(); + std::string fileContent = buffer.str(); + + std::regex pattern("export module ([a-zA-Z_\\-0-9\\.]*);"); + std::smatch match; + if (std::regex_search(fileContent, match, pattern)) { + modules.emplace_back(match[1], file, config, pcmDir, files, buildDir); + } + } + std::for_each(std::execution::par, modules.begin(), modules.end(), [&clangDir, &config, &pcmDir, &target, &march, &flags, &buildDir](Module& modulee) { + modulee.Check(); + modulee.Compile(clangDir, config, pcmDir, target, march, flags, buildDir); + }); + return modules; +} \ No newline at end of file diff --git a/Crafter.Build-ModuleFile.cppm b/Crafter.Build-ModuleFile.cppm index f182213..dfb0227 100644 --- a/Crafter.Build-ModuleFile.cppm +++ b/Crafter.Build-ModuleFile.cppm @@ -8,7 +8,7 @@ modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, +This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. @@ -23,23 +23,45 @@ module; #include #include #include +#include +#include +#include +#include export module Crafter.Build:ModuleFile; import :Configuration; namespace fs = std::filesystem; export namespace Crafter::Build { - class ModuleFile { - public: - inline static std::mutex allFilesMutex; - std::mutex fileMutex; - inline static std::unordered_map allFiles; - bool needsRecompiling; - bool recompiled = false; + class Module; + + class ModulePartition { + public: + bool needsRecompilingDependency = false; + bool needsRecompiling = false; fs::path path; - std::vector dependencies; - static ModuleFile* CompileModuleFile(fs::path path, std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target); - private: - ModuleFile(fs::path path, std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target); - void Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target); + Module* parent; + std::vector moduleDependencies; + std::vector partitionDependants; + std::vector partitionDependencies; + std::vector partitionDependenciesP; + const std::string name; + std::atomic* compiled = new std::atomic(false); + ModulePartition(const std::string& name, const fs::path& path, Module* parent, const std::string& fileContent, const fs::path& pcmDir); + void AddDependants(); + void Check(); + void Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags); + void CompileSource(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, const fs::path& buildDir); + }; + + class Module { + public: + bool needsRecompiling = false; + std::unordered_map partitions; + const std::string name; + const fs::path path; + Module(const std::string& name, const fs::path& path, const Configuration& config, const fs::path& pcmDir, std::string& files, const fs::path& buildDir); + void Check(); + void Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, const fs::path& buildDir); + static std::vector GetModules(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, std::string& files, const fs::path& buildDir); }; } diff --git a/Crafter.Build-Project.cpp b/Crafter.Build-Project.cpp index 41587cf..689c6a0 100644 --- a/Crafter.Build-Project.cpp +++ b/Crafter.Build-Project.cpp @@ -28,6 +28,7 @@ module; #include #include #include +#include module Crafter.Build; using namespace Crafter::Build; namespace fs = std::filesystem; @@ -90,8 +91,16 @@ void Project::Build(Configuration config, fs::path outputDir, fs::path binDir) c } if (!fs::exists(outputDir)) { fs::create_directory(outputDir); + } else{ + for (const auto& entry : fs::directory_iterator(outputDir)) { + fs::remove_all(entry); + } } + std::string buildDir = config.buildDir/fs::path(config.name); + if (!fs::exists(buildDir)) { + fs::create_directory(buildDir); + } std::string target; if(!config.target.empty()){ target = std::format("-target {}", config.target); @@ -100,7 +109,7 @@ void Project::Build(Configuration config, fs::path outputDir, fs::path binDir) c if(config.type == "library" || config.type == "shared-library"){ pcmDir = outputDir; }else{ - pcmDir = config.buildDir; + pcmDir = buildDir; } @@ -130,19 +139,19 @@ void Project::Build(Configuration config, fs::path outputDir, fs::path binDir) c for(std::int_fast32_t i = 0; i < depThreads.size(); i++) { if(config.dependencies[i].path.ends_with(".git")) { - std::string branch; - if(!config.dependencies[i].branch.empty()) { - fs::path name = fs::path(config.dependencies[i].path).filename(); - name.replace_extension(); - branch = std::format(" && cd {} && git switch {}", name.generic_string(), config.dependencies[i].branch); - } else if(!config.dependencies[i].commit.empty()){ - fs::path name = fs::path(config.dependencies[i].path).filename(); - name.replace_extension(); - branch = std::format(" && cd {} && git checkout {}", name.generic_string(), config.dependencies[i].commit); + fs::path name = fs::path(config.dependencies[i].path).filename(); + name.replace_extension(); + if(!fs::exists(buildDir/name)) { + if(!config.dependencies[i].branch.empty()) { + system(std::format("cd {} && git clone {} && cd {} && git switch {}", buildDir, config.dependencies[i].path, (buildDir/name).generic_string(), config.dependencies[i].branch).c_str()); + } else if(!config.dependencies[i].commit.empty()){ + system(std::format("cd {} && git clone {} && cd {} && git checkout {}", buildDir, config.dependencies[i].path, (buildDir/name).generic_string(), config.dependencies[i].commit).c_str()); + } + } else if(config.dependencies[i].commit.empty()) { + system(std::format("cd {} && git pull", (buildDir/name).generic_string()).c_str()); } - system(std::format("cd {} && git clone {}{}", config.buildDir, config.dependencies[i].path, branch).c_str()); config.dependencies[i].path = fs::path(config.dependencies[i].path).filename().replace_extension(); - Project project = Project::LoadFromJSON(fs::path(config.buildDir)/config.dependencies[i].path/"project.json"); + Project project = Project::LoadFromJSON(fs::path(buildDir)/config.dependencies[i].path/"project.json"); libs+=std::format(" -l{}", project.name); depThreads[i] = std::thread([i, pcmDir, config, project, binDir]() { project.Build(config.dependencies[i].configuration, pcmDir, binDir); @@ -154,7 +163,6 @@ void Project::Build(Configuration config, fs::path outputDir, fs::path binDir) c project.Build(config.dependencies[i].configuration, pcmDir, binDir); }); } - } std::string name = this->name; @@ -171,7 +179,7 @@ void Project::Build(Configuration config, fs::path outputDir, fs::path binDir) c name+=".wasm"; } } else { - clangDir = "clang++ -Wno-unused-command-line-argument"; + clangDir = "clang++"; } std::string flags; @@ -191,45 +199,25 @@ void Project::Build(Configuration config, fs::path outputDir, fs::path binDir) c thread.join(); } - std::string files; - //std::vector moduleThreads(config.moduleFiles.size()); - for(std::uint_fast32_t i = 0; i < config.moduleFiles.size(); i++) { - //moduleThreads[i] = std::thread([i, &config, pcmDir, target, clangDir](){ - ModuleFile::CompileModuleFile(config.moduleFiles[i], clangDir, config, pcmDir, target); - //}); - files+=std::format("{}.o ",(config.buildDir/config.moduleFiles[i].filename()).generic_string()); - } - // for(std::thread& thread : moduleThreads){ - // thread.join(); - // } - std::string march; if(config.target != "wasm32-unknown-wasi"){ march = std::format("-march={}", config.march); } + std::string files; + std::vector modules = Module::GetModules(clangDir, config, pcmDir, target, march, flags, files, buildDir); std::vector threads; - for(std::uint_fast32_t i = 0; i < config.sourceFiles.size(); i++) { - files+=std::format("{}_source.o ",(config.buildDir/config.sourceFiles[i].filename()).generic_string()); - //if(!fs::exists((config.buildDir/config.sourceFiles[i].filename()).generic_string()+"_source.o") || fs::last_write_time(config.sourceFiles[i].generic_string()+".cpp") > fs::last_write_time((config.buildDir/config.sourceFiles[i].filename()).generic_string()+"_source.o")) { - threads.emplace_back([i, &config, pcmDir, target, clangDir, flags, march](){ - std::string command = std::format("{} -std={} {}.cpp -fprebuilt-module-path={} -c -O{} {} {} -o {}_source.o {}", clangDir, config.standard, config.sourceFiles[i].generic_string(), pcmDir.generic_string(), config.optimizationLevel, march, flags, (config.buildDir/config.sourceFiles[i].filename()).generic_string(), target); - if(config.verbose) { - std::cout << command << std::endl; - } - system(command.c_str()); - }); - //} - } + + Source::GetSourceFiles(clangDir, config, pcmDir, target, march, flags, threads, modules, files, buildDir); for(std::uint_fast32_t i = 0; i < config.c_files.size(); i++) { - files+=std::format("{}_source.o ",(config.buildDir/config.c_files[i].filename()).generic_string()); - //if(!fs::exists((config.buildDir/config.sourceFiles[i].filename()).generic_string()+"_source.o") || fs::last_write_time(config.sourceFiles[i].generic_string()+".cpp") > fs::last_write_time((config.buildDir/config.sourceFiles[i].filename()).generic_string()+"_source.o")) { - threads.emplace_back([i, &config, pcmDir, target, clangDir, flags, march](){ - system(std::format("clang {}.c -c -O{} {} {} -o {}_source.o {}", config.c_files[i].generic_string(), config.optimizationLevel, march, flags, (config.buildDir/config.c_files[i].filename()).generic_string(), target).c_str()); + files+=std::format("{}_source.o ",(buildDir/config.c_files[i].filename()).generic_string()); + if(!fs::exists((buildDir/config.sourceFiles[i].filename()).generic_string()+"_source.o") || fs::last_write_time(config.sourceFiles[i].generic_string()+".c") > fs::last_write_time((config.buildDir/config.sourceFiles[i].filename()).generic_string()+"_source.o")) { + threads.emplace_back([i, &config, pcmDir, target, clangDir, flags, march, &buildDir](){ + system(std::format("clang {}.c -c -O{} {} {} -o {}_source.o {}", config.c_files[i].generic_string(), config.optimizationLevel, march, flags, (buildDir/config.c_files[i].filename()).generic_string(), target).c_str()); }); - //} + } } for(std::thread& thread : threads){ @@ -237,7 +225,6 @@ void Project::Build(Configuration config, fs::path outputDir, fs::path binDir) c } if(config.type == "executable"){ - //std::cout << std::format("{} {} {}-O{} -o {} {} {} -fuse-ld=lld", clangDir, flags, files, config.optimizationLevel, (outputDir/name).generic_string(), target, libs).c_str() << std::endl; if(config.target == "x86_64-w64-mingw64" || config.target == "x86_64-w64-mingw32") { flags += " -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread"; name += ".exe"; diff --git a/Crafter.Build-Shader.cpp b/Crafter.Build-Shader.cpp index 35e5107..997029b 100644 --- a/Crafter.Build-Shader.cpp +++ b/Crafter.Build-Shader.cpp @@ -36,6 +36,9 @@ Shader::Shader(fs::path path, std::string entrypoint, EShLanguage type): path(pa } void Shader::Compile(fs::path outputDir) { + if(!fs::exists((outputDir/path.filename()).replace_extension("spirv")) || fs::last_write_time(path) > fs::last_write_time((outputDir/path.filename()).replace_extension("spirv"))) { + return; + } glslang::InitializeProcess(); EShMessages messages = static_cast(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules); std::ifstream fileStream(path, std::ios::in | std::ios::binary); diff --git a/Crafter.Build-SourceFile.cpp b/Crafter.Build-SourceFile.cpp new file mode 100644 index 0000000..fcc09c5 --- /dev/null +++ b/Crafter.Build-SourceFile.cpp @@ -0,0 +1,87 @@ +/* +Crafter.Build +Copyright (C) 2025 Catcrafts® +Catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +module; +#include +#include +#include +#include +#include +#include +#include +module Crafter.Build; +using namespace Crafter::Build; +namespace fs = std::filesystem; + +void Source::GetSourceFiles(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, std::vector& threads, const std::vector& modules, std::string& files, const fs::path& buildDir) { + for(const fs::path& sourceFile : config.sourceFiles) { + files+=std::format("{}_source.o ",(buildDir/sourceFile.filename()).generic_string()); + if(Source::Check(sourceFile, modules, config, buildDir)) { + threads.emplace_back([&config, sourceFile, pcmDir, target, clangDir, flags, march, buildDir](){ + std::string command = std::format("{} -std={} {}.cpp -fprebuilt-module-path={} -c -O{} {} {} -o {}_source.o {}", clangDir, config.standard, sourceFile.generic_string(), pcmDir.generic_string(), config.optimizationLevel, march, flags, (buildDir/sourceFile.filename()).generic_string(), target); + if(config.verbose) { + std::cout << command << std::endl; + } + system(command.c_str()); + }); + } + } +} + +bool Source::Check(const fs::path& path, const std::vector& modules, const Configuration& config, const fs::path& buildDir) { + if(!fs::exists((buildDir/path.filename()).generic_string()+"_source.o") || fs::last_write_time((path).generic_string()+".cpp") > fs::last_write_time((buildDir/path.filename()).generic_string()+"_source.o")) { + return true; + } + std::ifstream t(path.generic_string()+".cpp"); + std::stringstream buffer; + buffer << t.rdbuf(); + std::string fileContent = buffer.str(); + { + std::regex pattern(R"(module ([a-zA-Z_\-0-9\.]*);)"); + std::sregex_iterator currentMatch(fileContent.begin(), fileContent.end(), pattern); + std::sregex_iterator lastMatch; + + while (currentMatch != lastMatch) { + std::smatch match = *currentMatch; + for(const Module& module : modules) { + if(module.name == match[1] && module.needsRecompiling) { + return true; + } + } + ++currentMatch; + } + } + { + std::regex pattern(R"(import ([a-zA-Z_\-0-9\.]*);)"); + std::sregex_iterator currentMatch(fileContent.begin(), fileContent.end(), pattern); + std::sregex_iterator lastMatch; + + while (currentMatch != lastMatch) { + std::smatch match = *currentMatch; + for(const Module& module : modules) { + if(module.name == match[1] && module.needsRecompiling) { + return true; + } + } + ++currentMatch; + } + } + return false; +} \ No newline at end of file diff --git a/Crafter.Build-SourceFile.cppm b/Crafter.Build-SourceFile.cppm new file mode 100644 index 0000000..36674e3 --- /dev/null +++ b/Crafter.Build-SourceFile.cppm @@ -0,0 +1,42 @@ +/* +Crafter.Build +Copyright (C) 2025 Catcrafts® +Catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +module; +#include +#include +#include +#include +#include +#include +#include +#include +export module Crafter.Build:SourceFile; +import :Configuration; +import :ModuleFile; +namespace fs = std::filesystem; + +namespace Crafter::Build { + class Source { + public: + static void GetSourceFiles(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, std::vector& threads, const std::vector& modules, std::string& files, const fs::path& buildDir); + private: + static bool Check(const fs::path& path, const std::vector& modules, const Configuration& config, const fs::path& buildDir); + }; +} \ No newline at end of file diff --git a/Crafter.Build.cppm b/Crafter.Build.cppm index 5d5cb87..d6ecefd 100644 --- a/Crafter.Build.cppm +++ b/Crafter.Build.cppm @@ -23,4 +23,5 @@ export import :Dependency; export import :Project; export import :Configuration; export import :ModuleFile; +export import :SourceFile; export import :Shader; diff --git a/build.sh b/build.sh index bcdb682..8175cdf 100755 --- a/build.sh +++ b/build.sh @@ -5,12 +5,14 @@ clang++ -std=c++26 Crafter.Build-Shader.cppm --precompile -fprebuilt-module-path clang++ -std=c++26 Crafter.Build-Dependency.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Dependency.pcm clang++ -std=c++26 Crafter.Build-Configuration.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Configuration.pcm clang++ -std=c++26 Crafter.Build-ModuleFile.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-ModuleFile.pcm +clang++ -std=c++26 Crafter.Build-SourceFile.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-SourceFile.pcm clang++ -std=c++26 Crafter.Build-Project.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Project.pcm clang++ -std=c++26 Crafter.Build.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build.pcm clang++ -std=c++26 Crafter.Build-Dependency.cpp -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Dependency_source.o clang++ -std=c++26 Crafter.Build-Configuration.cpp -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Configuration_source.o clang++ -std=c++26 Crafter.Build-ModuleFile.cpp -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-ModuleFile_source.o +clang++ -std=c++26 Crafter.Build-SourceFile.cpp -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-SourceFile_source.o clang++ -std=c++26 Crafter.Build-Shader.cpp -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Shader_source.o clang++ -std=c++26 Crafter.Build-Project.cpp -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Project_source.o clang++ -std=c++26 main.cpp -fprebuilt-module-path=./build -c -o ./build/main.o @@ -18,9 +20,10 @@ clang++ -std=c++26 main.cpp -fprebuilt-module-path=./build -c -o ./build/main.o clang++ -std=c++26 ./build/Crafter.Build-Project.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Project.o clang++ -std=c++26 ./build/Crafter.Build-Configuration.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Configuration.o clang++ -std=c++26 ./build/Crafter.Build-ModuleFile.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-ModuleFile.o +clang++ -std=c++26 ./build/Crafter.Build-SourceFile.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-SourceFile.o clang++ -std=c++26 ./build/Crafter.Build-Shader.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Shader.o clang++ -std=c++26 ./build/Crafter.Build-Dependency.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Dependency.o clang++ -std=c++26 ./build/Crafter.Build.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build.o -clang++ ./build/main.o ./build/Crafter.Build-ModuleFile_source.o ./build/Crafter.Build-ModuleFile.o ./build/Crafter.Build-Shader_source.o ./build/Crafter.Build-Shader.o ./build/Crafter.Build.o ./build/Crafter.Build-Configuration.o ./build/Crafter.Build-Configuration_source.o ./build/Crafter.Build-Project.o ./build/Crafter.Build-Project_source.o ./build/Crafter.Build-Dependency.o ./build/Crafter.Build-Dependency_source.o -O3 -o ./bin/crafter-build -L/usr/local/lib -L/usr/lib/ -lvulkan -lMachineIndependent -lOSDependent -lGenericCodeGen -lglslang -lglslang-default-resource-limits -lSPIRV -lSPVRemapper -fuse-ld=lld +clang++ ./build/main.o ./build/Crafter.Build-ModuleFile_source.o ./build/Crafter.Build-ModuleFile.o ./build/Crafter.Build-SourceFile_source.o ./build/Crafter.Build-SourceFile.o ./build/Crafter.Build-Shader_source.o ./build/Crafter.Build-Shader.o ./build/Crafter.Build.o ./build/Crafter.Build-Configuration.o ./build/Crafter.Build-Configuration_source.o ./build/Crafter.Build-Project.o ./build/Crafter.Build-Project_source.o ./build/Crafter.Build-Dependency.o ./build/Crafter.Build-Dependency_source.o -O3 -o ./bin/crafter-build -L/usr/local/lib -L/usr/lib/ -lvulkan -lMachineIndependent -lOSDependent -lGenericCodeGen -lglslang -lglslang-default-resource-limits -lSPIRV -lSPVRemapper -ltbb -fuse-ld=lld rm -rf build diff --git a/project.json b/project.json index a983993..1be8e3c 100644 --- a/project.json +++ b/project.json @@ -4,12 +4,11 @@ { "name": "base", "standard": "c++26", - "source_files": ["Crafter.Build-Configuration", "Crafter.Build-Project", "Crafter.Build-Dependency", "Crafter.Build-ModuleFile", "Crafter.Build-Shader"], - "module_files": ["Crafter.Build-Dependency", "Crafter.Build-Configuration", "Crafter.Build-Project", "Crafter.Build-Shader", "Crafter.Build", "Crafter.Build-ModuleFile"], - "build_dir": "./build", - "output_dir": "./bin", - "libs": ["vulkan", "MachineIndependent", "OSDependent", "GenericCodeGen", "glslang", "glslang-default-resource-limits", "SPIRV", "SPVRemapper"], - "verbose": true + "source_files": ["Crafter.Build-Configuration", "Crafter.Build-Project", "Crafter.Build-Dependency", "Crafter.Build-ModuleFile", "Crafter.Build-Shader", "Crafter.Build-SourceFile"], + "module_files": ["Crafter.Build-Dependency", "Crafter.Build-Configuration", "Crafter.Build-Project", "Crafter.Build-Shader", "Crafter.Build", "Crafter.Build-ModuleFile", "Crafter.Build-SourceFile"], + "build_dir": "build", + "output_dir": "bin", + "libs": ["vulkan", "MachineIndependent", "OSDependent", "GenericCodeGen", "glslang", "glslang-default-resource-limits", "SPIRV", "SPVRemapper", "tbb"] }, { "name": "executable",