diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index be91256..0000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: Main - -on: - push: - tags: - - '*' - -jobs: - build: - runs-on: self-hosted - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build - run: ./build.sh - - name: Release - - uses: actions/forgejo-release@v2 - with: - direction: upload - url: https://forgejo.catcrafts.net - repo: "Crafter/Crafter.Build" - tag: "${{ github.ref_name }}" - sha: "${{ github.sha }}" - release-dir: bin - token: ${{ secrets.TOKEN }} \ No newline at end of file diff --git a/Crafter.Build-Bounce.cppm b/Crafter.Build-Bounce.cppm deleted file mode 100644 index 2a6e50c..0000000 --- a/Crafter.Build-Bounce.cppm +++ /dev/null @@ -1,89 +0,0 @@ -/* -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:Bounce; - -namespace Crafter::Build { - export std::string RunCommand(const std::string_view cmd) { - std::array buffer; - std::string result; - - std::string with = std::string(cmd) + " 2>&1"; - // Open pipe to file - FILE* pipe = popen(with.c_str(), "r"); - if (!pipe) throw std::runtime_error("popen() failed!"); - - // Read till end of process: - while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { - result += buffer.data(); - } - - // Close pipe - pclose(pipe); - return result; - } - - export void RunCommandIgnore(const std::string_view cmd) { - std::string with = std::string(cmd) + " > /dev/null 2>&1"; - FILE* pipe = popen(with.c_str(), "r"); - if (!pipe) throw std::runtime_error("popen() failed!"); - pclose(pipe); - } - - export struct ClangError { - std::string filename; - uint32_t line_number; - std::string error_message; - std::string code; - }; - - export std::vector RunClang(const std::string_view cmd) { - std::string result = RunCommand(cmd); - std::vector errors; - - std::regex error_regex(R"((/[^:]+\.cpp):(\d+):\d+: error: (.*)\n\s*[0-9| ]*\s*(.*))"); - std::smatch match; - - while (std::regex_search(result, match, error_regex)) { - ClangError error; - error.filename = match[1].str(); - error.line_number = std::stoi(match[2].str()); - error.error_message = match[3].str(); - error.code = match[4].str(); - errors.push_back(error); - result = match.suffix().str(); - } - - if(result != "" && errors.size() == 0) { - throw std::runtime_error(result); - } - - return errors; - } -} \ No newline at end of file diff --git a/Crafter.Build-Configuration.cpp b/Crafter.Build-Configuration.cpp deleted file mode 100644 index c7bbf9d..0000000 --- a/Crafter.Build-Configuration.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* -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 "json.hpp" -#include -module Crafter.Build; -using namespace Crafter::Build; -namespace fs = std::filesystem; - -Configuration::Configuration(std::string name, std::string standard, std::vector sourceFiles, std::vector moduleFiles, std::string optimizationLevel, std::string buildDir, std::string outputDir, std::string type, std::string target, std::string march, std::vector dependencies, std::vector additionalFiles, std::vector flags, bool debug, std::vector libs, std::vector lib_paths, std::vector c_files, std::vector shaderFiles, std::vector includeDirs, bool verbose, std::vector defines): name(name), standard(standard), sourceFiles(sourceFiles), moduleFiles(moduleFiles), optimizationLevel(optimizationLevel), buildDir(buildDir), outputDir(outputDir), type(type), target(target), march(march), dependencies(dependencies), additionalFiles(additionalFiles), flags(flags), debug(debug), libs(libs), lib_paths(lib_paths), c_files(c_files), shaderFiles(shaderFiles), includeDirs(includeDirs), verbose(verbose), defines(defines) { - -} - -Configuration::Configuration(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir) { - name = config["name"].get(); - for (auto& [key, val] : config.items()) - { - if(key == "standard"){ - standard = val.get(); - } else if(key == "target") { - target = val.get(); - } else if(key == "debug") { - debug = val.get(); - } else if(key == "verbose") { - verbose = val.get(); - } else if(key == "type") { - type = val.get(); - } else if(key == "march") { - march = val.get(); - } else if(key == "source_files") { - const std::vector tempSourceFiles = val.get>(); - sourceFiles = std::vector(tempSourceFiles.size()); - for(std::int_fast32_t i = 0; i < sourceFiles.size(); i++){ - const std::filesystem::path filePath (tempSourceFiles[i]); - const std::filesystem::path fullFilePath = workingDir / filePath; - sourceFiles[i] = fullFilePath.generic_string(); - } - } else if(key == "c_files") { - const std::vector tempSourceFiles = val.get>(); - c_files = std::vector(tempSourceFiles.size()); - for(std::int_fast32_t i = 0; i < c_files.size(); i++){ - const std::filesystem::path filePath (tempSourceFiles[i]); - const std::filesystem::path fullFilePath = workingDir / filePath; - c_files[i] = fullFilePath.generic_string(); - } - } else if(key == "flags") { - flags = val.get>(); - } else if(key == "lib_paths") { - lib_paths = val.get>(); - } else if(key == "include_dirs") { - includeDirs = val.get>(); - } else if(key == "libs") { - libs = val.get>(); - } else if(key == "module_files") { - const std::vector tempModuleFiles = val.get>(); - moduleFiles = std::vector(tempModuleFiles.size()); - for(std::int_fast32_t i = 0; i < moduleFiles.size(); i++){ - const std::filesystem::path filePath (tempModuleFiles[i]); - const std::filesystem::path fullFilePath = workingDir / filePath; - moduleFiles[i] = fullFilePath.generic_string(); - } - } else if(key == "shaders") { - for (auto it : val) { - const std::filesystem::path filePath (it["path"].get()); - const std::filesystem::path fullFilePath = workingDir / filePath; - shaderFiles.emplace_back(fullFilePath, it["entrypoint"].get(), static_cast(it["type"].get())); - } - } else if(key == "defines") { - for (auto it : val) { - std::string name = it["name"].get(); - std::string value = it["value"].get(); - defines.emplace_back(name, value); - } - } else if(key == "additional_files") { - const std::vector tempAdditionalFiles = val.get>(); - additionalFiles = std::vector(tempAdditionalFiles.size()); - for(std::int_fast32_t i = 0; i < additionalFiles.size(); i++){ - const std::filesystem::path filePath (tempAdditionalFiles[i]); - const std::filesystem::path fullFilePath = workingDir / filePath; - additionalFiles[i] = fullFilePath.generic_string(); - } - } else if(key == "optimization_level") { - optimizationLevel = val.get(); - } else if(key == "build_dir") { - const std::string tempBuildDir = val.get(); - const std::filesystem::path buildPath (tempBuildDir); - const std::filesystem::path fullBuildPath = workingDir / buildPath; - buildDir = fullBuildPath.generic_string(); - } else if(key == "output_dir") { - const std::string tempOutputDir = val.get(); - const std::filesystem::path outputPath (tempOutputDir); - const std::filesystem::path fullOutputPath = workingDir / outputPath; - outputDir = fullOutputPath.generic_string(); - } else if(key == "dependencies") { - for (auto it : val) { - std::string commit; - std::string branch; - if(it.contains("commit")){ - commit = it["commit"].get(); - } - if(it.contains("branch")){ - branch = it["branch"].get(); - } - dependencies.emplace_back(it["path"].get(), it["configuration"].get(), commit, branch); - } - } else if(key != "extends") { - additionalProperties.insert({key, val}); - } - } - if(config.contains("extends")) { - const std::vector extends = config["extends"].get>(); - for(const std::string& extendName : extends) { - for (auto it : configs) { - if(it["name"].get() == extendName) { - Configuration extendData = Configuration(configs, it, workingDir); - if(!extendData.standard.empty() && standard.empty()){ - standard = extendData.standard; - } - if(!extendData.sourceFiles.empty()){ - sourceFiles.insert(sourceFiles.end(), extendData.sourceFiles.begin(), extendData.sourceFiles.end()); - } - if(!extendData.moduleFiles.empty()){ - moduleFiles.insert(moduleFiles.end(), extendData.moduleFiles.begin(), extendData.moduleFiles.end()); - } - if(!extendData.additionalFiles.empty()){ - additionalFiles.insert(additionalFiles.end(), extendData.additionalFiles.begin(), extendData.additionalFiles.end()); - } - if(!extendData.optimizationLevel.empty() && optimizationLevel.empty()){ - optimizationLevel = extendData.optimizationLevel; - } - if(!extendData.dependencies.empty()){ - dependencies.insert(dependencies.end(), extendData.dependencies.begin(), extendData.dependencies.end()); - } - if(!extendData.buildDir.empty() && buildDir.empty()) { - buildDir = extendData.buildDir; - } - if(!extendData.outputDir.empty() && outputDir.empty()) { - outputDir = extendData.outputDir; - } - if(!extendData.target.empty() && target.empty()) { - target = extendData.target; - } - if(!extendData.type.empty() && type.empty()) { - type = extendData.type; - } - if(!extendData.march.empty() && march.empty()) { - march = extendData.march; - } - if(!extendData.lib_paths.empty()){ - lib_paths.insert(lib_paths.end(), extendData.lib_paths.begin(), extendData.lib_paths.end()); - } - if(!extendData.c_files.empty()){ - c_files.insert(c_files.end(), extendData.c_files.begin(), extendData.c_files.end()); - } - if(!extendData.flags.empty()){ - flags.insert(flags.end(), extendData.flags.begin(), extendData.flags.end()); - } - if(extendData.debug){ - debug = true; - } - if(extendData.verbose){ - verbose = true; - } - if(!extendData.libs.empty()){ - libs.insert(libs.end(), extendData.libs.begin(), extendData.libs.end()); - } - if(!extendData.shaderFiles.empty()){ - shaderFiles.insert(shaderFiles.end(), extendData.shaderFiles.begin(), extendData.shaderFiles.end()); - } - if(!extendData.includeDirs.empty()){ - includeDirs.insert(includeDirs.end(), extendData.includeDirs.begin(), extendData.includeDirs.end()); - } - if(!extendData.defines.empty()){ - defines.insert(defines.end(), extendData.defines.begin(), extendData.defines.end()); - } - break; - } - } - } - } -} diff --git a/Crafter.Build-Configuration.cppm b/Crafter.Build-Configuration.cppm deleted file mode 100644 index bee8d6c..0000000 --- a/Crafter.Build-Configuration.cppm +++ /dev/null @@ -1,69 +0,0 @@ -/* -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 "json.hpp" -#include -#include -export module Crafter.Build:Configuration; -import :Dependency; -import :Shader; - -namespace fs = std::filesystem; - -export namespace Crafter::Build { - struct Define { - std::string name; - std::string value; - std::string ToString() const { - return std::format(" -D {}={}", name, value); - } - }; - - class Configuration { - public: - std::string name; - std::string standard; - std::vector sourceFiles; - std::vector c_files; - std::vector moduleFiles; - std::vector shaderFiles; - std::vector additionalFiles; - std::vector includeDirs; - std::vector defines; - std::string optimizationLevel; - std::string buildDir; - std::string outputDir; - std::string type; - std::string target; - std::string march; - bool debug; - std::vector libs; - std::vector lib_paths; - std::vector dependencies; - std::unordered_map additionalProperties; - std::vector flags; - bool verbose = false; - Configuration(std::string name, std::string standard, std::vector sourceFiles, std::vector moduleFiles, std::string optimizationLevel, std::string buildDir, std::string outputDir, std::string type, std::string target, std::string march, std::vector dependencies, std::vector additionalFiles, std::vector flags, bool debug, std::vector libs, std::vector lib_paths, std::vector c_files, std::vector shaderFiles, std::vector includeDirs, bool verbose, std::vector defines); - Configuration(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir); - }; -} diff --git a/Crafter.Build-ModuleFile.cpp b/Crafter.Build-ModuleFile.cpp deleted file mode 100644 index 127cc61..0000000 --- a/Crafter.Build-ModuleFile.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* -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 -module Crafter.Build; -using namespace Crafter::Build; -namespace fs = std::filesystem; - -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::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; - } - } - { - 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; - } else if(fs::last_write_time(path.generic_string()+".cppm") > fs::last_write_time((pcmDir/path.filename()).generic_string()+".pcm")) { - needsRecompiling = true; - } -} - -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 { - throw std::runtime_error(std::format("Partition {}:{} not found", parent->name, dependency)); - } - } -} - -void ModulePartition::Check() { - if(!needsRecompiling) { - for(ModulePartition* dependency : partitionDependenciesP) { - if(dependency->needsRecompiling) { - needsRecompiling = true; - break; - } - } - } -} - -std::vector ModulePartition::Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags) { - std::vector errors; - if(needsRecompiling) { - std::string defines; - for(const Define& define : config.defines) { - defines+=define.ToString(); - } - 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, defines, flags, config.standard, path.generic_string(), march, pcmDir.generic_string(), (pcmDir/path.filename()).generic_string(), target); - if(config.verbose) { - std::cout << command << std::endl; - } - errors = RunClang(command); - } - *compiled = true; - compiled->notify_all(); - return errors; -} -std::vector 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 defines; - for(const Define& define : config.defines) { - defines+=define.ToString(); - } - std::string command = std::format("{} {} -std={} {}.pcm -fprebuilt-module-path={} -c -O{} {} {} -o {}.o {}", clangDir, defines, 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; - } - return RunClang(command); -} - -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.needsRecompiling) { - needsRecompiling = true; - for(auto& [key, val] : partitions) { - val.needsRecompiling = true; - } - break; - } - } -} - -std::vector 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::string defines; - for(const Define& define : config.defines) { - defines+=define.ToString(); - } - - std::mutex errorMutex; - std::vector errors; - std::vector threads; - for(auto& [key, val] : partitions) {; - if(val.needsRecompiling) { - threads.emplace_back([&val, &clangDir, &config, &pcmDir, &target, &march, &flags, &errors, &errorMutex](){ - std::vector newErrors = val.Compile(clangDir, config, pcmDir, target, march, flags); - if(newErrors.size() > 0) { - errorMutex.lock(); - errors.insert(errors.end(), newErrors.begin(), newErrors.end()); - errorMutex.unlock(); - } - }); - } - } - - for(std::thread& thread : threads){ - thread.join(); - } - - if(errors.size() > 0) { - return errors; - } - - { - std::string command = std::format("{} {} {} -std={} {}.cppm --precompile {} -fprebuilt-module-path={} -o {}.pcm {}", clangDir, defines, flags, config.standard, path.generic_string(), march, pcmDir.generic_string(), (pcmDir/path.filename()).generic_string(), target); - if(config.verbose) { - std::cout << command << std::endl; - } - errors = RunClang(command); - } - - if(errors.size() > 0) { - return errors; - } - - std::vector threads2; - for(auto& [key, val] : partitions) {; - if(val.needsRecompiling) { - threads2.emplace_back([&val, &clangDir, &config, &pcmDir, &target, &march, &flags, &buildDir, &errors, &errorMutex](){ - std::vector newErrors = val.CompileSource(clangDir, config, pcmDir, target, march, flags, buildDir); - if(newErrors.size() > 0) { - errorMutex.lock(); - errors.insert(errors.end(), newErrors.begin(), newErrors.end()); - errorMutex.unlock(); - } - }); - } - } - threads2.emplace_back([this, &clangDir, &config, &pcmDir, &target, &march, &flags, &buildDir, &defines, &errors, &errorMutex](){ - std::string command = std::format("{} {} -std={} {}.pcm -fprebuilt-module-path={} -c -O{} {} {} -o {}.o {}", clangDir, defines, 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; - } - std::vector newErrors = RunClang(command); - if(newErrors.size() > 0) { - errorMutex.lock(); - errors.insert(errors.end(), newErrors.begin(), newErrors.end()); - errorMutex.unlock(); - } - }); - - for(std::thread& thread : threads2){ - thread.join(); - } - - return errors; -} - -std::tuple, 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::vector errors; - std::mutex errorMutex; - std::for_each(std::execution::par, modules.begin(), modules.end(), [&clangDir, &config, &pcmDir, &target, &march, &flags, &buildDir, &errors, &errorMutex](Module& modulee) { - modulee.Check(); - std::vector newErrors = modulee.Compile(clangDir, config, pcmDir, target, march, flags, buildDir); - if(newErrors.size() > 0) { - errorMutex.lock(); - errors.insert(errors.end(), newErrors.begin(), newErrors.end()); - errorMutex.unlock(); - } - }); - return {modules, errors}; -} \ No newline at end of file diff --git a/Crafter.Build-ModuleFile.cppm b/Crafter.Build-ModuleFile.cppm deleted file mode 100644 index 3ad1a6e..0000000 --- a/Crafter.Build-ModuleFile.cppm +++ /dev/null @@ -1,68 +0,0 @@ -/* -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 -#include -export module Crafter.Build:ModuleFile; -import :Configuration; -import :Bounce; -namespace fs = std::filesystem; - -export namespace Crafter::Build { - class Module; - - class ModulePartition { - public: - bool needsRecompiling = false; - fs::path path; - 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(); - std::vector Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags); - std::vector 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(); - std::vector 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::tuple, 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 deleted file mode 100644 index 47c036d..0000000 --- a/Crafter.Build-Project.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* -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 "json.hpp" -#include -#include -#include -#include -#include -#include -#include -module Crafter.Build; -using namespace Crafter::Build; -namespace fs = std::filesystem; - -Project::Project(std::string name, fs::path path, std::vector configurations) : name(name), path(path), configurations(configurations) { - -} - -std::tuple> Project::Build(std::string configuration) { - for(Configuration& config : configurations) { - if(config.name == configuration){ - return {config, Build(config)}; - } - } - throw std::runtime_error("Configuration: " + configuration + " not found."); -} - -std::tuple> Project::Build(std::string configuration, fs::path outputDir) { - for(Configuration& config : configurations) { - if(config.name == configuration){ - return {config, Build(config, outputDir)}; - } - } - throw std::runtime_error("Configuration: " + configuration + " not found."); -} - -std::tuple> Project::Build(std::string configuration, fs::path outputDir, fs::path binDir) { - for(Configuration& config : configurations) { - if(config.name == configuration){ - return {config, Build(config, outputDir, binDir)}; - } - } - throw std::runtime_error("Configuration: " + configuration + " not found."); -} - -std::vector Project::Build(Configuration& configuration) { - return Build(configuration, configuration.outputDir); -} - -std::vector Project::Build(Configuration& config, fs::path outputDir) { - return Build(config, outputDir, outputDir); -} - -std::vector Project::Build(Configuration& config, fs::path outputDir, fs::path binDir) { - if(config.standard.empty()) { - config.standard = "c++26"; - } - if(config.march.empty()) { - config.march = "native"; - } - if(config.type.empty()) { - config.type = "executable"; - } - - if (!fs::exists(config.buildDir)) { - fs::create_directory(config.buildDir); - } - if (!fs::exists(outputDir)) { - fs::create_directory(outputDir); - } - - if (!fs::exists(config.buildDir)) { - fs::create_directory(config.buildDir); - } - 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); - } - fs::path pcmDir; - if(config.type == "library" || config.type == "shared-library"){ - pcmDir = outputDir; - }else{ - pcmDir = buildDir; - } - - - std::string libs = " -L/usr/local/lib"; - - if(config.target != "x86_64-w64-mingw64" && config.target != "x86_64-w64-mingw32") { - libs += " -L/usr/lib/"; - } - - for(const std::string& path : config.lib_paths) { - libs += std::format(" -L{}", path); - } - - for(const std::string& path : config.includeDirs) { - libs += std::format(" -I{}", path); - } - - for(Shader& shader : config.shaderFiles) { - shader.Compile(binDir); - } - - std::vector depThreads = std::vector(config.dependencies.size()); - - if(config.dependencies.size() > 0){ - libs += std::format(" -L{}", pcmDir.generic_string()); - } - std::unordered_set depLibSet; - for(std::int_fast32_t i = 0; i < depThreads.size(); i++) { - if(config.dependencies[i].path.ends_with(".git")) { - 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()){ - std::cout << std::format("cd {} && git clone {} && cd {} && git checkout {}", buildDir, config.dependencies[i].path, (buildDir/name).generic_string(), config.dependencies[i].commit).c_str() << std::endl; - 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 { - system(std::format("cd {} && git clone {}", buildDir, config.dependencies[i].path).c_str()); - } - } else if(config.dependencies[i].commit.empty()) { - system(std::format("cd {} && git pull", (buildDir/name).generic_string()).c_str()); - } - config.dependencies[i].path = fs::path(config.dependencies[i].path).filename().replace_extension(); - Project project = Project::LoadFromJSON(fs::path(buildDir)/config.dependencies[i].path/"project.json"); - config.dependencies[i].name = project.name; - if (depLibSet.insert(project.name).second) { - libs+=std::format(" -l{}", project.name); - } - std::tuple> depConfig = project.Build(config.dependencies[i].configuration, pcmDir, binDir); - if(std::get<1>(depConfig).size() > 0) { - return std::get<1>(depConfig); - } - for(const std::string& lib2 : std::get<0>(depConfig).libs) { - if (depLibSet.insert(lib2).second) { - libs+=std::format(" -l{}", lib2); - } - } - for(const Dependency& dep2 : std::get<0>(depConfig).dependencies) { - if (depLibSet.insert(dep2.name).second) { - libs+=std::format(" -l{}", dep2.name); - } - } - } else{ - Project project = Project::LoadFromJSON(config.dependencies[i].path); - config.dependencies[i].name = project.name; - - if (depLibSet.insert(project.name).second) { - libs+=std::format(" -l{}", project.name); - } - std::tuple> depConfig = project.Build(config.dependencies[i].configuration, pcmDir, binDir); - if(std::get<1>(depConfig).size() > 0) { - return std::get<1>(depConfig); - } - for(const std::string& lib2 : std::get<0>(depConfig).libs) { - if (depLibSet.insert(lib2).second) { - libs+=std::format(" -l{}", lib2); - } - } - for(const Dependency& dep2 : std::get<0>(depConfig).dependencies) { - if (depLibSet.insert(dep2.name).second) { - libs+=std::format(" -l{}", dep2.name); - } - } - } - } - - std::string name = this->name; - - std::string clangDir; - if(config.target == "wasm32-unknown-wasi" || config.target == "wasm64-unknown-wasi"){ - clangDir = "${WASI_SDK_PATH}/bin/clang++ --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot -Wno-unused-command-line-argument -Wl,--export-all -fno-exceptions"; - if(config.type != "library") { - name+=".wasm"; - } - } else if(config.target == "wasm32" || config.target == "wasm64") { - clangDir = "${WASI_SDK_PATH}/bin/clang++ --no-standard-libraries -Wl,--no-entry -Wl,--export-all -Wno-unused-command-line-argument -fno-exceptions"; - if(config.type != "library") { - name+=".wasm"; - } - } else { - clangDir = "clang++"; - } - - std::string flags; - for(const std::string& flag : config.flags) { - flags+=flag; - } - - if(config.debug) { - flags+=" -g"; - } - - for(const std::string& lib : config.libs) { - libs+= std::format(" -l{}",lib); - } - - std::string march; - if(config.target != "wasm32-unknown-wasi"){ - march = std::format("-march={}", config.march); - } - - std::string files; - std::tuple, std::vector> modules = Module::GetModules(clangDir, config, pcmDir, target, march, flags, files, buildDir); - - if(std::get<1>(modules).size() > 0) { - return std::get<1>(modules); - } - - std::vector errors = Source::GetSourceFiles(clangDir, config, pcmDir, target, march, flags, std::get<0>(modules), files, buildDir); - if(errors.size() > 0) { - return errors; - } - - std::vector threads; - - for(std::uint_fast32_t i = 0; i < config.c_files.size(); i++) { - files+=std::format("{}_source.o ",(buildDir/config.c_files[i].filename()).generic_string()); - if(!fs::exists((buildDir/config.c_files[i].filename()).generic_string()+"_source.o") || fs::last_write_time(config.c_files[i].generic_string()+".c") > fs::last_write_time((buildDir/config.c_files[i].filename()).generic_string()+"_source.o")) { - threads.emplace_back([i, &config, pcmDir, target, clangDir, flags, march, &buildDir](){ - RunCommand(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){ - thread.join(); - } - - if(config.type == "executable"){ - std::string command = std::format("{} {} {}-O{} -o {} {} {} -fuse-ld=lld", clangDir, flags, files, config.optimizationLevel, (outputDir/name).generic_string(), target, libs); - if(config.verbose) { - std::cout << command << std::endl; - } - std::vector errors = RunClang(command); - if(errors.size() > 0) { - return errors; - } - } else if(config.type == "library"){ - RunCommandIgnore(std::format("ar r {}.a {}", (outputDir/fs::path("lib"+name)).generic_string(), files).c_str()); - } else if(config.type == "shared-library"){ - RunCommandIgnore(std::format("ar r {}.so {}", (outputDir/fs::path("lib"+name)).generic_string(), files).c_str()); - } - - for(const fs::path& additionalFile : config.additionalFiles){ - if(!fs::exists(binDir/additionalFile.filename())) { - fs::copy(additionalFile, binDir); - } else if (fs::last_write_time(additionalFile) > fs::last_write_time(binDir/additionalFile.filename())){ - fs::remove(binDir/additionalFile.filename()); - fs::copy(additionalFile, binDir); - } - } - return {}; -} - -Project Project::LoadFromJSON(fs::path path) { - if (!fs::exists(path)) { - throw std::runtime_error(std::format("Project file: {} not found.", path.generic_string())); - } - - std::ifstream f(path); - nlohmann::json data = nlohmann::json::parse(f); - const std::string name = data["name"].get(); - std::vector configurations; - nlohmann::json configs = data["configurations"]; - - const fs::path workingDir = path.remove_filename(); - for (nlohmann::json::iterator it = configs.begin(); it != configs.end(); ++it) { - configurations.emplace_back(configs, (*it), workingDir); - } - return Project(name, workingDir, configurations); -} diff --git a/Crafter.Build-Project.cppm b/Crafter.Build-Project.cppm deleted file mode 100644 index 8e33a30..0000000 --- a/Crafter.Build-Project.cppm +++ /dev/null @@ -1,47 +0,0 @@ -/* -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 -export module Crafter.Build:Project; -import :Configuration; -import :Bounce; -namespace fs = std::filesystem; - -export namespace Crafter::Build { - class Project { - public: - std::string name; - fs::path path; - std::vector configurations; - Project(std::string name, fs::path path, std::vector configurations); - std::tuple> Build(std::string configuration); - std::tuple> Build(std::string configuration, fs::path outputDir); - std::tuple> Build(std::string configuration, fs::path outputDir, fs::path binDir); - std::vector Build(Configuration& configuration); - std::vector Build(Configuration& configuration, fs::path outputDir); - std::vector Build(Configuration& configuration, fs::path outputDir, fs::path binDir); - void SaveToJSON(fs::path path); - static Project LoadFromJSON(fs::path path); - }; -} diff --git a/Crafter.Build-Shader.cpp b/Crafter.Build-Shader.cpp deleted file mode 100644 index 2453bc6..0000000 --- a/Crafter.Build-Shader.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 "DirStackFileIncluder.h" -#include -#include -module Crafter.Build; -using namespace Crafter::Build; - -Shader::Shader(fs::path path, std::string entrypoint, EShLanguage type): path(path), entrypoint(entrypoint), type(type) { - -} - -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"))) { - glslang::InitializeProcess(); - EShMessages messages = static_cast(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules); - std::ifstream fileStream(path, std::ios::in | std::ios::binary); - if (!fileStream) { - throw std::ios_base::failure("Failed to open file: " + path.string()); - } - std::ostringstream contents; - contents << fileStream.rdbuf(); - std::string src = contents.str(); - - const char *file_name_list[1] = {""}; - const char *shader_source = reinterpret_cast(src.data()); - - glslang::TShader shader(type); - shader.setStringsWithLengthsAndNames(&shader_source, nullptr, file_name_list, 1); - shader.setEntryPoint(entrypoint.c_str()); - shader.setSourceEntryPoint(entrypoint.c_str()); - shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4); - DirStackFileIncluder includeDir; - includeDir.pushExternalLocalDirectory(path.parent_path().generic_string()); - std::string info_log; - if (!shader.parse(GetDefaultResources(), 100, false, messages, includeDir)) - { - info_log = std::string(shader.getInfoLog()) + std::string(shader.getInfoDebugLog()); - } - // Add shader to new program object. - glslang::TProgram program; - program.addShader(&shader); - - // Link program. - if (!program.link(messages)) - { - info_log = std::string(program.getInfoLog()) + std::string(program.getInfoDebugLog()); - } - - // Save any info log that was generated. - if (shader.getInfoLog()) - { - info_log += std::string(shader.getInfoLog()) + std::string(shader.getInfoDebugLog()); - } - - if (program.getInfoLog()) - { - info_log += std::string(program.getInfoLog()) + std::string(program.getInfoDebugLog()); - } - - glslang::TIntermediate* intermediate = program.getIntermediate(type); - if (!intermediate) - { - info_log += "Failed to get shared intermediate code."; - } - - spv::SpvBuildLogger logger; - std::vector spirv; - std::cout << info_log; - glslang::GlslangToSpv(*intermediate, spirv, &logger); - std::cout << logger.getAllMessages(); - glslang::FinalizeProcess(); - fs::path filename = path.filename().replace_extension("spirv"); - std::ofstream file(outputDir/filename, std::ios::binary); - file.write(reinterpret_cast(spirv.data()), spirv.size() * sizeof(std::uint32_t)); - } -} diff --git a/Crafter.Build-SourceFile.cpp b/Crafter.Build-SourceFile.cpp deleted file mode 100644 index cf53674..0000000 --- a/Crafter.Build-SourceFile.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 -module Crafter.Build; -using namespace Crafter::Build; -namespace fs = std::filesystem; - -std::vector Source::GetSourceFiles(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, const std::vector& modules, std::string& files, const fs::path& buildDir) { - std::string defines; - for(const Define& define : config.defines) { - defines+=define.ToString(); - } - std::vector threads; - std::mutex errorMutex; - std::vector errors; - 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, defines, &errors, &errorMutex]() { - std::string command = std::format("{} {} -std={} {}.cpp -fprebuilt-module-path={} -c -O{} {} {} -o {}_source.o {}", clangDir, defines, 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; - } - std::vector newErrors = RunClang(command); - if(newErrors.size() > 0) { - errorMutex.lock(); - errors.insert(errors.end(), newErrors.begin(), newErrors.end()); - errorMutex.unlock(); - } - }); - } - } - for(std::thread& thread : threads){ - thread.join(); - } - return errors; -} - -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 deleted file mode 100644 index 14e6326..0000000 --- a/Crafter.Build-SourceFile.cppm +++ /dev/null @@ -1,42 +0,0 @@ -/* -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 std::vector GetSourceFiles(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, 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/README.md b/README.md index d392d57..e084c12 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,10 @@ This is a simple and easy to use C++ build system for Linux designed for use wit ## Prerequisites: ``` lld -clang 18> +clang git -libpqxx +lldb onetbb -boost glslang ``` diff --git a/build.sh b/build.sh index c99a518..562791f 100755 --- a/build.sh +++ b/build.sh @@ -1,31 +1,79 @@ mkdir build mkdir bin -clang++ -std=c++26 Crafter.Build-Bounce.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Bounce.pcm -clang++ -std=c++26 Crafter.Build-Shader.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Shader.pcm -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 +GCC_VERSION=$(g++ -dumpversion) +STD_HEADER="/usr/include/c++/$GCC_VERSION/bits/std.cc" +if [ ! -f "$STD_HEADER" ]; then + echo "Cannot find std.cc for GCC $GCC_VERSION" + exit 1 +fi +cp "$STD_HEADER" ./build/std.cppm +clang++ -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile ./build/std.cppm -o ./build/std.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-FixedVector.cppm -o ./build/Crafter.Build-FixedVector.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-Command.cppm -o ./build/Crafter.Build-Command.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-Shader.cppm -o ./build/Crafter.Build-Shader.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-Module.cppm -o ./build/Crafter.Build-Module.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-Implementation.cppm -o ./build/Crafter.Build-Implementation.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-Configuration.cppm -o ./build/Crafter.Build-Configuration.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-Test.cppm -o ./build/Crafter.Build-Test.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build interfaces/Crafter.Build-Project.cppm -o ./build/Crafter.Build-Project.pcm +clang++ -std=c++26 --precompile -fprebuilt-module-path=./build -D CRAFTER_BUILD_CONFIGURATION_TYPE_EXECUTABLE interfaces/Crafter.Build.cppm -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 +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-FixedVector.pcm -o ./build/Crafter.Build-FixedVector.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Command.pcm -o ./build/Crafter.Build-Command.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Shader.pcm -o ./build/Crafter.Build-Shader.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Module.pcm -o ./build/Crafter.Build-Module.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Implementation.pcm -o ./build/Crafter.Build-Implementation.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Configuration.pcm -o ./build/Crafter.Build-Configuration.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Project.pcm -o ./build/Crafter.Build-Project.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Test.pcm -o ./build/Crafter.Build-Test.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build.pcm -o ./build/Crafter.Build.o -clang++ -std=c++26 ./build/Crafter.Build-Bounce.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Bounce.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-Bounce.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 +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Command.cpp -o ./build/Crafter.Build-Command_impl.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Test.cpp -o ./build/Crafter.Build-Test_impl.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Implementation.cpp -o ./build/Crafter.Build-Implementation_impl.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Shader.cpp -o ./build/Crafter.Build-Shader_impl.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Module.cpp -o ./build/Crafter.Build-Module_impl.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Configuration.cpp -o ./build/Crafter.Build-Configuration_impl.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Project.cpp -o ./build/Crafter.Build-Project_impl.o +clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/main.cpp -o ./build/main.o + +clang++ -std=c++26 -O3 -L/usr/local/lib -L/usr/lib/ -lvulkan -lMachineIndependent -lOSDependent -lGenericCodeGen -lglslang -lglslang-default-resource-limits -lSPIRV -ltbb -fuse-ld=lld ./build/Crafter.Build-Command.o ./build/Crafter.Build-FixedVector.o ./build/Crafter.Build-Shader.o ./build/Crafter.Build-Module.o ./build/Crafter.Build-Configuration.o ./build/Crafter.Build-Project.o ./build/Crafter.Build.o ./build/Crafter.Build-Command_impl.o ./build/Crafter.Build-Shader_impl.o ./build/Crafter.Build-Module_impl.o ./build/Crafter.Build-Configuration_impl.o ./build/Crafter.Build-Project_impl.o ./build/Crafter.Build-Implementation.o ./build/Crafter.Build-Implementation_impl.o ./build/Crafter.Build-Test_impl.o ./build/Crafter.Build-Test.o ./build/main.o -o ./bin/crafter-build rm -rf build + + +# clang++ -std=c++26 implementations/Crafter.Build-Command.cppm -stdlib=libc++ --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Command_impl.pcm +# clang++ -std=c++26 implementations/Crafter.Build-Shader.cppm -stdlib=libc++ --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Shader_impl.pcm +# clang++ -std=c++26 implementations/Crafter.Build-Module.cppm -stdlib=libc++ --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Module_impl.pcm +# clang++ -std=c++26 implementations/Crafter.Build-Configuration.cppm -stdlib=libc++ --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Configuration_impl.pcm +# clang++ -std=c++26 implementations/Crafter.Build-Project.cppm -stdlib=libc++ --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Project_impl.pcm +# clang++ -std=c++26 implementations/Crafter.Build.cppm -stdlib=libc++ --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build_impl.pcm + +# clang++ -std=c++26 Crafter.Build-Shader.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Shader.pcm +# 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 + +# clang++ -std=c++26 ./build/Crafter.Build-Bounce.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build-Bounce.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-Bounce.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 -ltbb -fuse-ld=lld + +# rm -rf build diff --git a/implementations/Crafter.Build-Command.cpp b/implementations/Crafter.Build-Command.cpp new file mode 100644 index 0000000..41f5e77 --- /dev/null +++ b/implementations/Crafter.Build-Command.cpp @@ -0,0 +1,90 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 +module Crafter.Build:Command_impl; +import :Command; +import std; + + +namespace Crafter { + std::string RunCommand(const std::string_view cmd) { + std::array buffer; + std::string result; + + std::string with = std::string(cmd) + " 2>&1"; + // Open pipe to file + FILE* pipe = popen(with.c_str(), "r"); + if (!pipe) throw std::runtime_error("popen() failed!"); + + // Read till end of process: + while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { + result += buffer.data(); + } + + // Close pipe + pclose(pipe); + return result; + } + + void RunCommandIgnore(const std::string_view cmd) { + std::string with = std::string(cmd) + " > /dev/null 2>&1"; + FILE* pipe = popen(with.c_str(), "r"); + if (!pipe) throw std::runtime_error("popen() failed!"); + pclose(pipe); + } + + CompileException::CompileException(std::vector&& errors) : errors(std::move(errors)) { + for(CompileError error : errors) { + message += std::format("File: {}:{}\nMessage: {}\nCode: {}", error.filename, error.line, error.message, error.code); + } + }; + const char* CompileException::what() const noexcept { + return message.c_str(); + } + + void RunClang(const std::string_view cmd) { + std::cout << cmd << std::endl; + std::string result = RunCommand(cmd); + std::cout << result << std::endl; + // std::vector errors; + + // std::regex error_regex(R"((/[^:]+\.cpp):(\d+):\d+: error: (.*)\n\s*[0-9| ]*\s*(.*))"); + // std::smatch match; + + // while (std::regex_search(result, match, error_regex)) { + // CompileError error; + // error.filename = match[1].str(); + // error.line = std::stoi(match[2].str()); + // error.message = match[3].str(); + // error.code = match[4].str(); + // errors.push_back(error); + // result = match.suffix().str(); + // } + + if(result != "") { + // if(errors.size() != 0) { + // throw CompileException(std::move(errors)); + // } else { + throw std::runtime_error(result); + //} + } + } +} \ No newline at end of file diff --git a/implementations/Crafter.Build-Configuration.cpp b/implementations/Crafter.Build-Configuration.cpp new file mode 100644 index 0000000..61531df --- /dev/null +++ b/implementations/Crafter.Build-Configuration.cpp @@ -0,0 +1,241 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 "../lib/json.hpp" +#include +module Crafter.Build:Configuration_impl; +import :Configuration; +import std; +//import :Dependency; +import :Shader; +import :Module; + +namespace fs = std::filesystem; + +namespace Crafter { + Configuration::Configuration(std::string&& name) : name(std::move(name)) { + + } + Configuration::Configuration(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir) { + SetDataFromJson(configs, config, workingDir); + } + + void Configuration::SetDataFromJson(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir) { + if(config.contains("extends")) { + const std::vector extends = config["extends"].get>(); + for(const std::string& extendName : extends) { + for (auto it : configs) { + if(it["name"].get() == extendName) { + SetDataFromJson(configs, it, workingDir); + } + } + } + } + if(config.contains("name")) { + name = config["name"].get(); + } else { + throw std::runtime_error("Invalid config json, name field is missing"); + } + if(config.contains("standard")) { + standard = config["standard"].get(); + } + if(config.contains("target")) { + target = config["target"].get(); + } + if(config.contains("debug")) { + debug = config["debug"].get(); + } + if(config.contains("type")) { + std::string typeString = config["type"].get(); + if(typeString == "executable") { + type = CRAFTER_CONFIGURATION_TYPE_EXECUTABLE; + } else if(typeString == "library") { + type = CRAFTER_CONFIGURATION_TYPE_LIBRARY; + } else if(typeString == "shared-library") { + type = CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY; + } else { + throw std::invalid_argument("Unknown type: " + typeString); + } + } + if(config.contains("march")) { + march = config["march"].get(); + } + if(config.contains("libs")) { + for (auto it : config["libs"]) { + libs.push_back(it.get()); + } + } + if(config.contains("interfaces")) { + const std::vector tempModuleFiles = config["interfaces"].get>(); + std::vector> tempModulePaths = std::vector>(tempModuleFiles.size()); + for(std::uint_fast32_t i = 0; i < tempModuleFiles.size(); i++){ + const std::filesystem::path file = workingDir / (tempModuleFiles[i]+".cppm"); + std::ifstream t(file); + std::stringstream buffer; + buffer << t.rdbuf(); + std::string fileContent = buffer.str(); + fileContent = std::regex_replace(fileContent, std::regex(R"(//[^\n]*)"), ""); + fileContent = std::regex_replace(fileContent, std::regex(R"(/\*.*?\*/)"), ""); + tempModulePaths[i] = {file, fileContent, nullptr, nullptr}; + } + + std::erase_if(tempModulePaths, [this](std::tuple& file) { + std::smatch match; + if (std::regex_search(std::get<1>(file), match, std::regex(R"(export module ([a-zA-Z0-9_\.\-]+);)"))) { + std::get<0>(file).replace_extension(""); + interfaces.push_back(std::make_unique(std::move(match[1].str()), std::move(std::get<0>(file)))); + return true; + } else { + return false; + } + }); + + for(uint_fast32_t i = 0; i < tempModulePaths.size(); i++) { + std::smatch match; + if (std::regex_search(std::get<1>(tempModulePaths[i]), match, std::regex(R"(export module ([a-zA-Z_0-9\.\-]+):([a-zA-Z_0-9\.\-]+);)"))) { + for(const std::unique_ptr& modulee : interfaces) { + if(modulee->name == match[1]) { + std::string name = match[2].str(); + fs::path pthCpy = std::get<0>(tempModulePaths[i]); + pthCpy.replace_extension(""); + std::unique_ptr partition = std::make_unique(std::move(name), std::move(pthCpy)); + std::get<2>(tempModulePaths[i]) = partition.get(); + modulee->partitions.push_back(std::move(partition)); + std::get<3>(tempModulePaths[i]) = modulee.get(); + goto next; + } + } + throw std::runtime_error(std::format("Module {} not found, referenced in {}", match[1].str(), std::get<0>(tempModulePaths[i]).string())); + } else { + throw std::runtime_error(std::format("No module declaration found in {}", std::get<0>(tempModulePaths[i]).string())); + } + next:; + } + + for(std::tuple& file : tempModulePaths) { + std::regex pattern(R"(import :([a-zA-Z_\-0-9\.]+);)"); + std::sregex_iterator currentMatch(std::get<1>(file).begin(), std::get<1>(file).end(), pattern); + std::sregex_iterator lastMatch; + + while (currentMatch != lastMatch) { + std::smatch match = *currentMatch; + for(std::unique_ptr& partition2 : std::get<3>(file)->partitions) { + if(partition2->name == match[1]) { + std::get<2>(file)->partitionDependencies.push_back(partition2.get()); + goto next2; + } + } + throw std::runtime_error(std::format("imported partition {}:{} not found, referenced in {}", std::get<3>(file)->name, match[1].str(), std::get<0>(file).string())); + next2: ++currentMatch; + } + } + } + if(config.contains("implementations")) { + const std::vector tempFiles = config["implementations"].get>(); + for(const std::string& tempFile : tempFiles) { + const std::filesystem::path file = workingDir / (tempFile + ".cpp"); + std::ifstream t(file); + std::stringstream buffer; + buffer << t.rdbuf(); + std::string fileContent = buffer.str(); + fileContent = std::regex_replace(fileContent, std::regex(R"(//[^\n]*)"), ""); + fileContent = std::regex_replace(fileContent, std::regex(R"(/\*.*?\*/)"), ""); + std::smatch match; + fs::path fileCopy = file; + fileCopy.replace_extension(""); + Implementation& implementation = implementations.emplace_back(std::move(fileCopy)); + if (std::regex_search(fileContent, match, std::regex(R"(module ([a-zA-Z0-9_\.\-]+))"))) { + for(const std::unique_ptr& interface : interfaces) { + if(interface->name == match[1]) { + 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 match2 = *currentMatch; + for(const std::unique_ptr& partition : interface->partitions) { + if(partition->name == match2[1]) { + implementation.partitionDependencies.push_back(partition.get()); + goto next3; + } + } + throw std::runtime_error(std::format("imported partition {}:{} not found, referenced in {}", match[1].str(), match2[1].str(), file.string())); + next3: ++currentMatch; + } + goto next4; + } + } + throw std::runtime_error(std::format("Module {} not found not found, referenced in {}", match[1].str(), file.string())); + next4:; + } else { + 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 std::unique_ptr& interface : interfaces) { + if(interface->name == match[1]) { + implementation.moduleDependencies.push_back(interface.get()); + goto next5; + } + } + //throw std::runtime_error(std::format("imported module {} not found, referenced in {}", match[1].str(), file.string())); + next5: ++currentMatch; + } + } + } + } + if(config.contains("shaders")) { + for (auto it : config["shaders"]) { + shaders.emplace_back(workingDir / it["path"].get(), it["entrypoint"].get(), static_cast(it["type"].get())); + } + } + if(config.contains("defines")) { + for (auto it : config["defines"]) { + defines.emplace_back(it["name"].get(), it["value"].get()); + } + } + if(config.contains("additional_files")) { + const std::vector tempAdditionalFiles = config["additional_files"].get>(); + for (const std::string& file : tempAdditionalFiles) { + additionalFiles.push_back( workingDir / file); + } + } + if(config.contains("build_dir")) { + buildDir = workingDir / config["build_dir"].get(); + } + if(config.contains("output_dir")) { + outputDir = workingDir / config["output_dir"].get(); + } + if(config.contains("dependencies")) { + for (auto it : config["dependencies"]) { + std::string commit; + std::string branch; + if(it.contains("commit")){ + commit = it["commit"].get(); + } + if(it.contains("branch")){ + branch = it["branch"].get(); + } + dependencies.emplace_back(it["path"].get(), it["configuration"].get(), std::move(commit), std::move(branch)); + } + } + } +} diff --git a/implementations/Crafter.Build-Implementation.cpp b/implementations/Crafter.Build-Implementation.cpp new file mode 100644 index 0000000..52e9e6d --- /dev/null +++ b/implementations/Crafter.Build-Implementation.cpp @@ -0,0 +1,61 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 Crafter.Build:Implementation_impl; +import std; +import :Implementation; +import :Module; +import :Command; +namespace fs = std::filesystem; + +namespace Crafter { + Implementation::Implementation(fs::path&& path) : path(std::move(path)) { + + } + bool Implementation::Check(const fs::path& buildDir, const fs::path& pcmDir) const { + if(fs::exists((buildDir/path.filename()).string()+"_impl.o") && fs::last_write_time(path.string()+".cpp") < fs::last_write_time((buildDir/path.filename()).string()+"_impl.o")) { + for(ModulePartition* dependency : partitionDependencies) { + if(dependency->Check(pcmDir)) { + return true; + } + } + for(Module* dependency : moduleDependencies) { + if(dependency->Check(pcmDir)) { + return true; + } + } + return false; + } else { + return true; + } + } + void Implementation::Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir) const { + for(ModulePartition* dependency : partitionDependencies) { + if(!dependency->compiled.load()) { + dependency->compiled.wait(false); + } + } + for(Module* dependency : moduleDependencies) { + if(!dependency->compiled.load()) { + dependency->compiled.wait(false); + } + } + RunClang(std::format("{} {}.cpp -fprebuilt-module-path={} -fprebuilt-module-path={} -c -o {}_impl.o", clang, path.string(), pcmDir.string(), buildDir.parent_path().string(), (buildDir/path.filename()).string())); + } +} diff --git a/implementations/Crafter.Build-Module.cpp b/implementations/Crafter.Build-Module.cpp new file mode 100644 index 0000000..68efdee --- /dev/null +++ b/implementations/Crafter.Build-Module.cpp @@ -0,0 +1,139 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 Crafter.Build:Module_impl; +import std; +import :Module; +import :Command; +namespace fs = std::filesystem; + +namespace Crafter { + ModulePartition::ModulePartition(std::string&& name, fs::path&& path) : name(std::move(name)), path(std::move(path)), compiled(false) {} + + bool ModulePartition::Check(const fs::path& pcmDir) { + if(!checked) { + if(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")) { + for(ModulePartition* dependency : partitionDependencies) { + if(dependency->Check(pcmDir)) { + needsRecompiling = true; + checked = true; + return true; + } + } + for(Module* dependency : moduleDependencies) { + if(dependency->Check(pcmDir)) { + needsRecompiling = true; + checked = true; + return true; + } + } + needsRecompiling = false; + compiled.store(true); + checked = true; + return false; + } else { + needsRecompiling = true; + checked = true; + return true; + } + } else { + return needsRecompiling; + } + } + + void ModulePartition::Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir) { + for(ModulePartition* dependency : partitionDependencies) { + if(!dependency->compiled.load()) { + //std::cout << std::format("{} is waiting on {}", name, dependency->name) << std::endl; + dependency->compiled.wait(false); + } + } + RunClang(std::format("{} {}.cppm --precompile -fprebuilt-module-path={} -fprebuilt-module-path={} -o {}.pcm", clang, path.string(), pcmDir.string(), buildDir.parent_path().string(), (pcmDir/path.filename()).string())); + RunClang(std::format("{} {}.pcm -fprebuilt-module-path={} -fprebuilt-module-path={} -c -o {}.o", clang, (pcmDir/path.filename()).string(), pcmDir.string(), buildDir.parent_path().string(), (buildDir/path.filename()).string())); + compiled.store(true); + compiled.notify_all(); + } + + Module::Module(std::string&& name, fs::path&& path, std::vector>&& partitions) : name(std::move(name)), path(std::move(path)), partitions(std::move(partitions)), compiled(false) {} + + Module::Module(std::string&& name, fs::path&& path) : name(std::move(name)), path(std::move(path)), compiled(false) {} + + Module::Module(fs::path&& path) : path(std::move(path)), compiled(false) { + std::ifstream t(this->path); + std::stringstream buffer; + buffer << t.rdbuf(); + std::string fileContent = buffer.str(); + fileContent = std::regex_replace(fileContent, std::regex(R"(//[^\n]*)"), ""); + fileContent = std::regex_replace(fileContent, std::regex(R"(/\*.*?\*/)"), ""); + std::smatch match; + if (std::regex_search(fileContent, match, std::regex(R"(export module ([a-zA-Z0-9_.-]+);)"))) { + name = match[1].str(); + } else { + throw std::runtime_error(std::format("No module declaration found in {}", this->path.string())); + } + } + + bool Module::Check(const fs::path& pcmDir) { + if(!checked) { + if(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")) { + for(std::unique_ptr& partition : partitions) { + if(partition->Check(pcmDir)) { + needsRecompiling = true; + checked = true; + return true; + } + } + needsRecompiling = false; + compiled.store(true); + checked = true; + return false; + } else { + for(std::unique_ptr& partition : partitions) { + partition->Check(pcmDir); + } + needsRecompiling = true; + checked = true; + return true; + } + } else { + return needsRecompiling; + } + } + + void Module::Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir) { + std::vector threads; + for(std::unique_ptr& partition : partitions) { + if(partition->needsRecompiling) { + threads.emplace_back([&partition, clang, &pcmDir, &buildDir](){ + partition->Compile(clang, pcmDir, buildDir); + }); + } + } + + for(std::thread& thread : threads){ + thread.join(); + } + + RunClang(std::format("{} {}.cppm --precompile -fprebuilt-module-path={} -fprebuilt-module-path={} -o {}.pcm", clang, path.string(), pcmDir.string(), buildDir.parent_path().string(), (pcmDir/path.filename()).string())); + RunClang(std::format("{} {}.pcm -fprebuilt-module-path={} -fprebuilt-module-path={} -c -o {}.o", clang, (pcmDir/path.filename()).string(), pcmDir.string(), buildDir.parent_path().string(), (buildDir/path.filename()).string())); + + compiled.store(true); + compiled.notify_all(); + } +} diff --git a/implementations/Crafter.Build-Project.cpp b/implementations/Crafter.Build-Project.cpp new file mode 100644 index 0000000..6909e35 --- /dev/null +++ b/implementations/Crafter.Build-Project.cpp @@ -0,0 +1,384 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 "../lib/json.hpp" +#include +module Crafter.Build:Project_impl; +import :Project; +import std; +import :Configuration; +import :Command; +namespace fs = std::filesystem; + +namespace Crafter { + Project::Project(std::string&& name, fs::path&& path, std::vector&& configurations) : name(std::move(name)), path(std::move(path)), configurations(std::move(configurations)) {} + + Configuration& Project::Build(std::string_view configuration) { + for(Configuration& config : configurations) { + if(config.name == configuration){ + Build(config); + return config; + } + } + throw std::runtime_error(std::format("Configuration: {} not found.", configuration)); + } + Configuration& Project::Build(std::string_view configuration, const fs::path& outputDir) { + for(Configuration& config : configurations) { + if(config.name == configuration){ + Build(config, outputDir); + return config; + } + } + throw std::runtime_error(std::format("Configuration: {} not found.", configuration)); + } + Configuration& Project::Build(std::string_view configuration, const fs::path& outputDir, const fs::path& binDir) { + for(Configuration& config : configurations) { + if(config.name == configuration){ + Build(config, outputDir, binDir); + return config; + } + } + throw std::runtime_error(std::format("Configuration: {} not found.", configuration)); + } + Configuration& Project::Build(std::string_view configuration, const fs::path& outputDir, const fs::path& binDir, const fs::path& buildDir) { + for(Configuration& config : configurations) { + if(config.name == configuration){ + Build(config, outputDir, binDir, buildDir); + return config; + } + } + throw std::runtime_error(std::format("Configuration: {} not found.", configuration)); + } + void Project::Build(Configuration& config) const { + if(config.outputDir.empty()) { + Build(config, "bin"); + } else { + Build(config, config.outputDir); + } + } + void Project::Build(Configuration& config, const fs::path& outputDir) const { + Build(config, outputDir, outputDir); + } + void Project::Build(Configuration& config, const fs::path& outputDir, const fs::path& binDir) const { + fs::path buildDir; + if(config.buildDir.empty()) { + buildDir = "build"/fs::path(config.name); + } else { + buildDir = config.buildDir/config.name; + } + Build(config, outputDir, binDir, buildDir); + } + void Project::Build(Configuration& config, const fs::path& outputDir, const fs::path& binDir, const fs::path& buildDir) const { + if (!fs::exists(outputDir)) { + fs::create_directories(outputDir); + } + + if (!fs::exists(buildDir)) { + fs::create_directories(buildDir); + } + + std::vector threads; + for(const Shader& shader : config.shaders) { + if(shader.Check(outputDir)) { + threads.emplace_back(&Shader::Compile, &shader, binDir); + } + } + + std::thread fileThread([&config, &binDir](){ + for(const fs::path& additionalFile : config.additionalFiles){ + if(!fs::exists(binDir/additionalFile.filename())) { + fs::copy(additionalFile, binDir); + } else if (fs::last_write_time(additionalFile) > fs::last_write_time(binDir/additionalFile.filename())){ + fs::remove(binDir/additionalFile.filename()); + fs::copy(additionalFile, binDir); + } + } + }); + + + std::string command = "clang++"; + if(!config.target.empty()) { + command += std::format(" -target={}", config.target); + } + if(!config.march.empty()) { + command += std::format(" -march={}", config.march); + } + if(!config.standard.empty()) { + command += std::format(" -std={}", config.standard); + } else { + command += std::format(" -std=c++26"); + } + for(const Define& define : config.defines) { + command += std::format(" -D {}={}", define.name, define.value); + } + + fs::path pcmDir; + + if(config.type == CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY) { + command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY"; + pcmDir = outputDir; + } else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY) { + command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_LIBRARY"; + pcmDir = outputDir; + } else { + command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_EXECUTABLE"; + pcmDir = buildDir; + } + + if(config.debug) { + command += " -g -D CRAFTER_BUILD_CONFIGURATION_DEBUG"; + } else { + command += " -O3"; + } + + std::unordered_set depLibSet; + std::vector depThreads = std::vector(config.dependencies.size()); + std::mutex libMutex; + std::string libsString; + + for(std::uint_fast32_t i = 0; i < depThreads.size(); i++) { + depThreads[i] = std::thread([i, &config, &libMutex, &depLibSet, &buildDir, &pcmDir, &libsString, &binDir](){ + if(config.dependencies[i].path.ends_with(".git")) { + fs::path name = fs::path(config.dependencies[i].path).filename(); + name.replace_extension(); + if(!fs::exists(buildDir/name)) { + if(!config.dependencies[i].branch.empty()) { + RunCommandIgnore(std::format("cd {} && git clone {} && cd {} && git switch {}", buildDir.string(), config.dependencies[i].path, (buildDir/name).string(), config.dependencies[i].branch)); + } else if(!config.dependencies[i].commit.empty()){ + std::cout << std::format("cd {} && git clone {} && cd {} && git checkout {}", buildDir.string(), config.dependencies[i].path, (buildDir/name).string(), config.dependencies[i].commit) << std::endl; + RunCommandIgnore(std::format("cd {} && git clone {} && cd {} && git checkout {}", buildDir.string(), config.dependencies[i].path, (buildDir/name).string(), config.dependencies[i].commit)); + } else { + RunCommandIgnore(std::format("cd {} && git clone {}", buildDir.string(), config.dependencies[i].path)); + } + } else if(config.dependencies[i].commit.empty()) { + RunCommandIgnore(std::format("cd {} && git pull", (buildDir/name).string())); + } + config.dependencies[i].path = fs::path(config.dependencies[i].path).filename().replace_extension()/"project.json"; + } + Project project = Project::LoadFromJSON(config.dependencies[i].path); + for(Configuration& depConfig : project.configurations) { + if(depConfig.name == config.dependencies[i].configuration){ + fs::path depBuildDir; + if(depConfig.buildDir.empty()) { + depBuildDir = "build"/fs::path(config.name); + } else { + depBuildDir = depConfig.buildDir/config.name; + } + project.Build(depConfig, pcmDir, binDir, (fs::path(config.dependencies[i].path).parent_path()/depBuildDir/depConfig.name)); + libMutex.lock(); + if (depLibSet.insert(project.name).second) { + libsString+=std::format(" -l{}", project.name); + } + for(const std::string& lib2 : depConfig.libs) { + if (depLibSet.insert(lib2).second) { + libsString+=std::format(" -l{}", lib2); + } + } + for(const Dependency& dep2 : depConfig.dependencies) { + if (depLibSet.insert(project.name).second) { + libsString+=std::format(" -l{}", project.name); + } + } + libMutex.unlock(); + return; + } + } + throw std::runtime_error(std::format("Configuration: {} not found.", config.dependencies[i].configuration)); + }); + } + + if(!fs::exists((fs::path(buildDir).parent_path()/path.filename()).string()+".o")) { + std::string result = RunCommand(std::format(R"(GCC_VERSION=$(g++ -dumpversion) +STD_HEADER="/usr/include/c++/$GCC_VERSION/bits/std.cc" +if [ ! -f "$STD_HEADER" ]; then + echo "Cannot find std.cc for GCC $GCC_VERSION" + exit 1 +fi +cp "$STD_HEADER" {}/std.cppm +clang++ -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {}/std.cppm -o {}/std.pcm )", fs::path(buildDir).parent_path().string(), fs::path(buildDir).parent_path().string(), fs::path(buildDir).parent_path().string())); + if(result != "") { + throw std::runtime_error(result); + } + } + + for(std::thread& thread : depThreads) { + thread.join(); + } + + std::string files; + bool repack = false; + for(std::unique_ptr& modulee : config.interfaces) { + if(modulee->Check(pcmDir)) { + threads.emplace_back(&Module::Compile, modulee.get(), command, pcmDir, buildDir); + repack = true; + } + files += std::format(" {}/{}.o", buildDir.string(), modulee->path.filename().string()); + for(std::unique_ptr& part : modulee->partitions) { + files += std::format(" {}/{}.o", buildDir.string(), part->path.filename().string()); + } + } + + for(const Implementation& implementation : config.implementations) { + if(implementation.Check(buildDir, pcmDir)) { + repack = true; + threads.emplace_back(&Implementation::Compile, &implementation, command, pcmDir, buildDir); + } + files += std::format(" {}/{}_impl.o", buildDir.string(), implementation.path.filename().string()); + } + + for(std::thread& thread : threads) { + thread.join(); + } + + command += " -L/usr/local/lib"; + command += libsString; + + for(const std::string& lib : config.libs) { + depLibSet.insert(lib); + command += std::format(" -l{}", lib); + } + + fileThread.join(); + + if(config.dependencies.size() > 0){ + command += std::format(" -L{}", pcmDir.string()); + } + + if(repack) { + if(config.type == CRAFTER_CONFIGURATION_TYPE_EXECUTABLE){ + RunClang(std::format("{}{} -o {} -fuse-ld=lld", command, files, (outputDir/name).string())); + } else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY){ + std::cout << std::format("ar r {}.a {}", (outputDir/fs::path("lib"+name)).string(), files) << std::endl; + RunCommandIgnore(std::format("ar r {}.a {}", (outputDir/fs::path("lib"+name)).string(), files)); + } else { + RunClang(std::format("{}{} -shared -o {}.so -fuse-ld=lld", command, files, (outputDir/name).string())); + } + } + } + + Project Project::LoadFromJSON(const fs::path& path) { + if (!fs::exists(path)) { + throw std::runtime_error(std::format("Project file: {} not found.", path.generic_string())); + } + + std::ifstream f(path); + nlohmann::json data = nlohmann::json::parse(f); + std::string name = data["name"].get(); + std::vector configurations; + nlohmann::json configs = data["configurations"]; + std::vector tests; + nlohmann::json testJson = data["tests"]; + + fs::path workingDir = path; + workingDir.remove_filename(); + + for (nlohmann::json::iterator it = configs.begin(); it != configs.end(); ++it) { + configurations.emplace_back(configs, (*it), workingDir); + } + + for (nlohmann::json::iterator it = testJson.begin(); it != testJson.end(); ++it) { + tests.emplace_back(configs, (*it), workingDir); + } + + Project project(std::move(name), std::move(workingDir), std::move(configurations)); + project.tests = std::move(tests); + return project; + } + + std::vector Project::RunTests() { + std::vector results; + for(Test& test : tests) { + results.push_back(RunTest(test)); + } + return results; + } + + TestResult Project::RunTest(const std::string_view testName) { + for(Test& test : tests) { + if(test.config.name == testName){ + return RunTest(test); + } + } + throw std::runtime_error(std::format("Test: {} not found.", testName)); + } + + TestResult Project::RunTest(Test& test) { + fs::path buildDir; + if(test.config.buildDir.empty()) { + buildDir = "build"/fs::path(test.config.name); + } else { + buildDir = test.config.buildDir/test.config.name; + } + char path[PATH_MAX];// + ssize_t count = readlink("/proc/self/exe", path, PATH_MAX); + if (count == -1) { + throw std::runtime_error("Failed to get executable path"); + } + path[count] = '\0'; + if (!fs::exists(buildDir)) { + fs::create_directories(buildDir); + } + if(!fs::exists((fs::path(buildDir).parent_path()/this->path.filename()).string()+".o")) { + std::string result = RunCommand(std::format(R"(GCC_VERSION=$(g++ -dumpversion) +STD_HEADER="/usr/include/c++/$GCC_VERSION/bits/std.cc" +if [ ! -f "$STD_HEADER" ]; then + echo "Cannot find std.cc for GCC $GCC_VERSION" + exit 1 +fi +cp "$STD_HEADER" {}/std.cppm +clang++ -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {}/std.cppm -o {}/std.pcm )", fs::path(buildDir).parent_path().string(), fs::path(buildDir).parent_path().string(), fs::path(buildDir).parent_path().string())); + if(result != "") { + throw std::runtime_error(result); + } + } + // if(!fs::exists(buildDir.parent_path().string()+"/Crafter.Test.pcm")) { + // RunClang(std::format("clang++ -std=c++26 --precompile -fprebuilt-module-path={} {}/interfaces/Crafter.Test.cppm -o {}/Crafter.Test.pcm", buildDir.parent_path().string(), std::filesystem::absolute(path).parent_path().parent_path().string(), buildDir.parent_path().string())); + // } + Build(test.config, buildDir, buildDir); + std::string lib = std::format("{}/{}.so", buildDir.string(), name); + void* handle = dlopen(lib.c_str(), RTLD_NOW); + if (!handle) { + throw std::runtime_error(std::format("Failed to load test library, {}, {}", lib, dlerror())); + } + dlerror(); + typedef std::string* (*RunTestFunc)(); + RunTestFunc loadedTest = (RunTestFunc) dlsym(handle, "RunTest"); + const char* dlsym_error = dlerror(); + if (dlsym_error) { + dlclose(handle); + throw std::runtime_error(std::format("Cannot load symbol 'RunTest': {}", dlsym_error)); + } + std::string* testResult = loadedTest(); + if(testResult != nullptr) { + TestResult result = {test.config.name, *testResult}; + delete testResult; + return result; + } else { + return {test.config.name, ""}; + } + } + + // void AddModule(std::string_view configuration, std::string_view filename); + // void AddModule(Configuration& configuration, std::string_view filename); + // void AddModuleSourcePair(std::string_view configuration, std::string_view filename); + // void AddModuleSourcePair(Configuration& configuration, std::string_view filename); + // void AddTest(std::string_view configuration, std::string_view filename, std::string_view content); + // void AddTest(Configuration& configuration, std::string_view filename, std::string_view content); + // void SaveToJSON(const fs::path& path) const; +} diff --git a/implementations/Crafter.Build-Shader.cpp b/implementations/Crafter.Build-Shader.cpp new file mode 100644 index 0000000..2933c10 --- /dev/null +++ b/implementations/Crafter.Build-Shader.cpp @@ -0,0 +1,100 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 "../lib/DirStackFileIncluder.h" +module Crafter.Build:Shader_impl; +import :Shader; +import std; +namespace fs = std::filesystem; + +namespace Crafter { + Shader::Shader(fs::path&& path, std::string&& entrypoint, EShLanguage type) : path(std::move(path)), entrypoint(std::move(entrypoint)), type(type) { + + } + bool Shader::Check(const fs::path& outputDir) const { + return fs::exists((outputDir/path.filename()).generic_string()+".spv") && fs::last_write_time(path.generic_string()+".glsl") < fs::last_write_time((outputDir/path.filename()).generic_string()+".spv"); + } + void Shader::Compile(const fs::path& outputDir) const { + glslang::InitializeProcess(); + EShMessages messages = static_cast(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules); + std::ifstream fileStream(path, std::ios::in | std::ios::binary); + if (!fileStream) { + throw std::ios_base::failure("Failed to open file: " + path.string()); + } + std::ostringstream contents; + contents << fileStream.rdbuf(); + std::string src = contents.str(); + + const char *file_name_list[1] = {""}; + const char *shader_source = reinterpret_cast(src.data()); + + glslang::TShader shader(type); + shader.setStringsWithLengthsAndNames(&shader_source, nullptr, file_name_list, 1); + shader.setEntryPoint(entrypoint.c_str()); + shader.setSourceEntryPoint(entrypoint.c_str()); + shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4); + DirStackFileIncluder includeDir; + includeDir.pushExternalLocalDirectory(path.parent_path().generic_string()); + std::string info_log; + if (!shader.parse(GetDefaultResources(), 100, false, messages, includeDir)) + { + info_log = std::string(shader.getInfoLog()) + std::string(shader.getInfoDebugLog()); + } + // Add shader to new program object. + glslang::TProgram program; + program.addShader(&shader); + + // Link program. + if (!program.link(messages)) + { + info_log = std::string(program.getInfoLog()) + std::string(program.getInfoDebugLog()); + } + + // Save any info log that was generated. + if (shader.getInfoLog()) + { + info_log += std::string(shader.getInfoLog()) + std::string(shader.getInfoDebugLog()); + } + + if (program.getInfoLog()) + { + info_log += std::string(program.getInfoLog()) + std::string(program.getInfoDebugLog()); + } + + glslang::TIntermediate* intermediate = program.getIntermediate(type); + if (!intermediate) + { + info_log += "Failed to get shared intermediate code."; + } + + spv::SpvBuildLogger logger; + std::vector spirv; + std::cout << info_log; + glslang::GlslangToSpv(*intermediate, spirv, &logger); + std::cout << logger.getAllMessages(); + glslang::FinalizeProcess(); + fs::path filename = path.filename().replace_extension("spv"); + std::ofstream file(outputDir/filename, std::ios::binary); + file.write(reinterpret_cast(spirv.data()), spirv.size() * sizeof(std::uint32_t)); + } +} diff --git a/implementations/Crafter.Build-Test.cpp b/implementations/Crafter.Build-Test.cpp new file mode 100644 index 0000000..58f64f5 --- /dev/null +++ b/implementations/Crafter.Build-Test.cpp @@ -0,0 +1,34 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 "../lib/json.hpp" +module Crafter.Build:Test_impl; +import :Test; +import std; + +namespace fs = std::filesystem; + +namespace Crafter { + Test::Test(Configuration&& config) : config(std::move(config)) { + config.type = CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY; + } + Test::Test(const nlohmann::json& configs, const nlohmann::json& config, const fs::path& workingDir): config(configs, config, workingDir) { + this->config.type = CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY; + } +} diff --git a/implementations/main.cpp b/implementations/main.cpp new file mode 100644 index 0000000..5633633 --- /dev/null +++ b/implementations/main.cpp @@ -0,0 +1,105 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 +*/ +#include +import Crafter.Build; +import std; +using namespace Crafter; +namespace fs = std::filesystem; +int main(int argc, char* argv[]) { + if(std::string(argv[1]) == "--help") { + std::println("--help\tDisplays this help message.\n-c The name of the configuration to build.\n-p The path to the project file. defualts to project.json\n-o Overrides the output folder.\n-r Runs the executable after building."); + return 0; + } + if(argc < 2 && std::string(argv[1]) == "build") { + std::println("Too little arguments provided, use --help for help"); + return 0; + } + fs::path filepath = "project.json"; + std::string configuration; + std::string outputDir; + std::string command = std::string(argv[1]); + std::string argument; + if(argc > 2) { + argument = std::string(argv[2]); + } + + bool run = false; + for (std::uint_fast32_t i = 3; i < argc; i++) { + std::string arg = std::string(argv[i]); + if(arg == "-r"){ + run = true; + } else if(arg == "-o"){ + outputDir = argv[++i]; + } else if(arg == "-p"){ + filepath = fs::path(argv[++i]); + } else{ + std::println("Unkown argument: {}", argv[i]); + return 1; + } + } + + fs::path projectPath; + if(filepath.is_relative()){ + projectPath = fs::current_path()/filepath; + }else{ + projectPath = filepath; + } + + Project project = Project::LoadFromJSON(projectPath); + + if(command == "build") { + if(outputDir.empty()){ + project.Build(argument); + } else{ + project.Build(argument, fs::path(outputDir)); + } + if(run){ + for(Configuration& config : project.configurations) { + if(config.name == configuration) { + if(config.debug) { + system(std::format("cd {} && ./{}", (projectPath/fs::path(config.outputDir)).generic_string(), project.name).c_str()); + } else { + system(std::format("cd {} && lldb -o run {}", (projectPath/fs::path(config.outputDir)).generic_string(), project.name).c_str()); + } + return 0; + } + } + } + } else if(command == "test") { + if(argument.empty()) { + std::vector results = project.RunTests(); + for(const TestResult& result : results) { + if(result.message.empty()) { + std::cout << std::format("✅ {}", result.name) << std::endl; + } else { + std::cout << std::format("❌ {}\t{}", result.name, result.message) << std::endl; + } + } + } else { + TestResult result = project.RunTest(argument); + if(result.message.empty()) { + std::cout << std::format("✅ {}", result.name) << std::endl; + } else { + std::cout << std::format("❌ {}\t{}", result.name, result.message) << std::endl; + } + } + } else { + std::println("Unkown command: {}", command); + } +} \ No newline at end of file diff --git a/interfaces/Crafter.Build-Command.cppm b/interfaces/Crafter.Build-Command.cppm new file mode 100644 index 0000000..c024170 --- /dev/null +++ b/interfaces/Crafter.Build-Command.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 version 3.0 as published by the Free Software Foundation; + +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 +*/ + +export module Crafter.Build:Command; +import std; + +namespace Crafter { + export struct CompileError { + std::string filename; + std::uint_fast32_t line; + std::string message; + std::string code; + }; + + export class CompileException : public std::exception { + public: + std::string message; + std::vector errors; + CompileException(std::vector&& errors); + const char* what() const noexcept override; + }; + + export std::string RunCommand(const std::string_view cmd); + export void RunCommandIgnore(const std::string_view cmd); + export void RunClang(const std::string_view cmd); +} \ No newline at end of file diff --git a/interfaces/Crafter.Build-Configuration.cppm b/interfaces/Crafter.Build-Configuration.cppm new file mode 100644 index 0000000..e5cb39c --- /dev/null +++ b/interfaces/Crafter.Build-Configuration.cppm @@ -0,0 +1,72 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 "../lib/json.hpp" +export module Crafter.Build:Configuration; +import std; +import :Implementation; +import :Shader; +import :Module; + +namespace fs = std::filesystem; + +namespace Crafter { + export struct Define { + std::string name; + std::string value; + Define(std::string&& name, std::string&& value): name(std::move(name)), value(std::move(value)) { } + }; + + export class Dependency { + public: + std::string path; + std::string configuration; + std::string commit; + std::string branch; + Dependency(std::string&& path, std::string&& configuration, std::string&& commit, std::string&& branch): path(std::move(path)), configuration(std::move(configuration)), commit(std::move(commit)), branch(std::move(branch)) { } + };; + + export enum ConfigurationType { + CRAFTER_CONFIGURATION_TYPE_EXECUTABLE, + CRAFTER_CONFIGURATION_TYPE_LIBRARY, + CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY, + }; + + export class Configuration { + public: + std::string name; + std::string standard; + std::vector> interfaces; + std::vector implementations; + std::vector dependencies; + std::vector shaders; + std::vector additionalFiles; + std::vector defines; + fs::path buildDir; + fs::path outputDir; + ConfigurationType type; + std::string target; + std::string march; + bool debug; + std::vector libs; + Configuration(std::string&& name); + Configuration(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir); + void SetDataFromJson(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir); + }; +} diff --git a/interfaces/Crafter.Build-FixedVector.cppm b/interfaces/Crafter.Build-FixedVector.cppm new file mode 100644 index 0000000..a658f36 --- /dev/null +++ b/interfaces/Crafter.Build-FixedVector.cppm @@ -0,0 +1,82 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 +*/ + +export module Crafter.Build:FixedVector; +import std; +namespace fs = std::filesystem; + +namespace Crafter { + export template + class FixedVector { + public: + using value_type = T; + using iterator = T*; + using const_iterator = const T*; + T* values; + std::uint_fast32_t size_; + FixedVector() {} + FixedVector(std::uint_fast32_t size): values(reinterpet_cast(operator new[](size * sizeof(T)))), size_(size) {} + + void Set(std::uint_fast32_t size) { + #ifdef CRAFTER_BUILD_CONFIGURATION_DEBUG + if(this->size_ != 0) { + throw std::runtime_error("FixedVector already set!"); + } + #endif + values = reinterpet_cast(operator new[](size * sizeof(T))); + this->size_ = size; + } + + ~FixedVector() { + delete[] values; + } + + FixedVector(const FixedVector&) = delete; + FixedVector& operator=(const FixedVector&) = delete; + + FixedVector(FixedVector&& other) noexcept : values(other.values), size_(other.size_) { + other.values = nullptr; + other.size_ = 0; + } + + FixedVector& operator=(FixedVector&& other) noexcept { + if (this != &other) { + delete[] values; + values = other.values; + size_ = other.size_; + other.values = nullptr; + other.size_ = 0; + } + return *this; + } + + T& operator[](std::uint_fast32_t index) { return values[index]; } + const T& operator[](std::uint_fast32_t index) const { return values[index]; } + + std::uint_fast32_t size() const { return size_; } + + iterator begin() { return values; } + const_iterator begin() const { return values; } + const_iterator cbegin() const { return values; } + + iterator end() { return values + size_; } + const_iterator end() const { return values + size_; } + const_iterator cend() const { return values + size_; } + }; +} diff --git a/interfaces/Crafter.Build-Implementation.cppm b/interfaces/Crafter.Build-Implementation.cppm new file mode 100644 index 0000000..d773b57 --- /dev/null +++ b/interfaces/Crafter.Build-Implementation.cppm @@ -0,0 +1,36 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 +*/ + +export module Crafter.Build:Implementation; +import std; +namespace fs = std::filesystem; + +namespace Crafter { + export class Module; + export class ModulePartition; + export class Implementation { + public: + std::vector moduleDependencies; + std::vector partitionDependencies; + fs::path path; + Implementation(fs::path&& path); + bool Check(const fs::path& buildDir, const fs::path& pcmDir) const; + void Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir) const; + }; +} diff --git a/interfaces/Crafter.Build-Module.cppm b/interfaces/Crafter.Build-Module.cppm new file mode 100644 index 0000000..c62459f --- /dev/null +++ b/interfaces/Crafter.Build-Module.cppm @@ -0,0 +1,54 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 +*/ + +export module Crafter.Build:Module; +import std; +namespace fs = std::filesystem; + +namespace Crafter { + export class Module; + export class ModulePartition { + public: + std::vector moduleDependencies; + std::vector partitionDependencies; + std::atomic compiled; + bool needsRecompiling; + bool checked = false; + std::string name; + fs::path path; + ModulePartition(std::string&& name, fs::path&& path); + bool Check(const fs::path& pcmDir); + void Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir); + }; + + export class Module { + public: + std::atomic compiled; + bool needsRecompiling; + bool checked = false; + std::vector> partitions; + std::string name; + fs::path path; + Module(fs::path&& path); + Module(std::string&& name, fs::path&& path); + Module(std::string&& name, fs::path&& path, std::vector>&& partitions); + bool Check(const fs::path& pcmDir); + void Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir); + }; +} diff --git a/interfaces/Crafter.Build-Project.cppm b/interfaces/Crafter.Build-Project.cppm new file mode 100644 index 0000000..7fbf4d1 --- /dev/null +++ b/interfaces/Crafter.Build-Project.cppm @@ -0,0 +1,47 @@ +/* +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 version 3.0 as published by the Free Software Foundation; + +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 +*/ + +export module Crafter.Build:Project; +import std; +import :Configuration; +import :Test; +namespace fs = std::filesystem; + +namespace Crafter { + export class Project { + public: + std::string name; + fs::path path; + std::vector configurations; + std::vector tests; + Project(std::string&& name, fs::path&& path, std::vector&& configurations); + static Project LoadFromJSON(const fs::path& path); + Configuration& Build(std::string_view configuration); + Configuration& Build(std::string_view configuration, const fs::path& outputDir); + Configuration& Build(std::string_view configuration, const fs::path& outputDir, const fs::path& binDir); + Configuration& Build(std::string_view configuration, const fs::path& outputDir, const fs::path& binDir, const fs::path& builDir); + void Build(Configuration& configuration) const; + void Build(Configuration& configuration, const fs::path& outputDir) const; + void Build(Configuration& configuration, const fs::path& outputDir, const fs::path& binDir) const; + void Build(Configuration& configuration, const fs::path& outputDir, const fs::path& binDir, const fs::path& builDir) const; + TestResult RunTest(const std::string_view test); + TestResult RunTest(Test& test); + std::vector RunTests(); + }; +} diff --git a/Crafter.Build-Shader.cppm b/interfaces/Crafter.Build-Shader.cppm similarity index 61% rename from Crafter.Build-Shader.cppm rename to interfaces/Crafter.Build-Shader.cppm index abb18a2..5a19932 100644 --- a/Crafter.Build-Shader.cppm +++ b/interfaces/Crafter.Build-Shader.cppm @@ -1,14 +1,13 @@ /* -Crafter.Build +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. +License version 3.0 as published by the Free Software Foundation; - 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. @@ -19,19 +18,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#include -#include +#include #include +#include +#include "../lib/DirStackFileIncluder.h" export module Crafter.Build:Shader; +import std; namespace fs = std::filesystem; -export namespace Crafter::Build { - class Shader { +namespace Crafter { + export class Shader { public: fs::path path; std::string entrypoint; EShLanguage type; - Shader(fs::path path, std::string entrypoint, EShLanguage type); - void Compile(fs::path outputDir); + Shader(fs::path&& path, std::string&& entrypoint, EShLanguage type); + bool Check(const fs::path& outputDir) const; + void Compile(const fs::path& outputDir) const; }; } diff --git a/Crafter.Build-Dependency.cppm b/interfaces/Crafter.Build-Test.cppm similarity index 52% rename from Crafter.Build-Dependency.cppm rename to interfaces/Crafter.Build-Test.cppm index 835102c..f292665 100644 --- a/Crafter.Build-Dependency.cppm +++ b/interfaces/Crafter.Build-Test.cppm @@ -1,14 +1,13 @@ /* -Crafter.Build +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. +License version 3.0 as published by the Free Software Foundation; - 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. @@ -17,19 +16,24 @@ 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 -export module Crafter.Build:Dependency; +#include "../lib/json.hpp" +export module Crafter.Build:Test; +import std; +import :Configuration; -export namespace Crafter::Build { - class Dependency { - public: - std::string path; - std::string configuration; - std::string commit; - std::string branch; +namespace fs = std::filesystem; + +namespace Crafter { + export struct TestResult { std::string name; - Dependency(std::string path, std::string configuration, std::string commit, std::string branch); + std::string message; + }; + + export class Test { + public: + Configuration config; + Test(Configuration&& name); + Test(const nlohmann::json& configs, const nlohmann::json& config, const fs::path& workingDir); }; } diff --git a/Crafter.Build.cppm b/interfaces/Crafter.Build.cppm similarity index 68% rename from Crafter.Build.cppm rename to interfaces/Crafter.Build.cppm index b4d31a6..787ff92 100644 --- a/Crafter.Build.cppm +++ b/interfaces/Crafter.Build.cppm @@ -1,14 +1,13 @@ /* -Crafter.Build +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. +License version 3.0 as published by the Free Software Foundation; - 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. @@ -19,10 +18,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ export module Crafter.Build; -export import :Dependency; +export import :Command; export import :Project; +export import :Module; export import :Configuration; -export import :ModuleFile; -export import :SourceFile; export import :Shader; -export import :Bounce; +export import :Implementation; +export import :FixedVector; +export import :Test; \ No newline at end of file diff --git a/DirStackFileIncluder.h b/lib/DirStackFileIncluder.h similarity index 100% rename from DirStackFileIncluder.h rename to lib/DirStackFileIncluder.h diff --git a/json.hpp b/lib/json.hpp similarity index 100% rename from json.hpp rename to lib/json.hpp diff --git a/main.cpp b/main.cpp deleted file mode 100644 index d0019ee..0000000 --- a/main.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* -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 -*/ - -#include -#include -#include -#include -#include "json.hpp" -import Crafter.Build; -using namespace Crafter::Build; -namespace fs = std::filesystem; -int main(int argc, char* argv[]) { - if(argc == 1) { - std::println("No arguments provided, use --help for help"); - } - fs::path filepath = "project.json"; - std::string configuration; - std::string outputDir; - bool run = false; - for (std::uint_fast32_t i = 1; i < argc; i++) { - std::string arg = std::string(argv[i]); - if(arg == "--help"){ - std::println("--help\tDisplays this help message.\n-c The name of the configuration to build.\n-p The path to the project file. defualts to project.json\n-o Overrides the output folder.\n-r Runs the executable after building."); - return 0; - } else if(arg == "-c"){ - configuration = argv[++i]; - } else if(arg == "-r"){ - run = true; - } else if(arg == "-o"){ - outputDir = argv[++i]; - } else if(arg == "-p"){ - filepath = fs::path(argv[++i]); - } else{ - std::println("Unkown argument: {}", argv[i]); - return 1; - } - } - - fs::path projectPath; - if(filepath.is_relative()){ - projectPath = fs::current_path()/filepath; - }else{ - projectPath = filepath; - } - - std::vector errors; - Project project = Project::LoadFromJSON(projectPath); - if(outputDir.empty()){ - errors = std::get<1>(project.Build(configuration)); - } else{ - errors = std::get<1>(project.Build(configuration, fs::path(outputDir))); - } - - if(errors.size() > 0){ - for (const ClangError& error : errors) { - std::cout << "Filename: " << error.filename << std::endl; - std::cout << "Line: " << error.line_number << std::endl; - std::cout << "Error Message: " << error.error_message << std::endl; - std::cout << "Code: " << error.code << std::endl << std::endl; - } - return 0; - } - - if(run){ - for(Configuration& config : project.configurations) { - if(config.name == configuration) { - if(config.target == "x86_64-w64-mingw64" || config.target == "x86_64-w64-mingw32") { - project.name += ".exe"; - } - system(std::format("cd {} && ./{}", (projectPath/fs::path(config.outputDir)).generic_string(), project.name).c_str()); - return 0; - } - } - } -} diff --git a/project.json b/project.json index 2062cb5..eccc447 100644 --- a/project.json +++ b/project.json @@ -3,18 +3,20 @@ "configurations": [ { "name": "base", - "standard": "c++26", - "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", "Crafter.Build-Bounce"], - "build_dir": "build", - "output_dir": "bin", - "libs": ["vulkan", "MachineIndependent", "OSDependent", "GenericCodeGen", "glslang", "glslang-default-resource-limits", "SPIRV", "SPVRemapper", "tbb"] + "interfaces": ["interfaces/Crafter.Build-Command", "interfaces/Crafter.Build-Configuration", "interfaces/Crafter.Build-Module", "interfaces/Crafter.Build-Project", "interfaces/Crafter.Build-Shader", "interfaces/Crafter.Build", "interfaces/Crafter.Build-Implementation", "interfaces/Crafter.Build-FixedVector", "interfaces/Crafter.Build-Test"], + "implementations": ["implementations/Crafter.Build-Command", "implementations/Crafter.Build-Configuration", "implementations/Crafter.Build-Module", "implementations/Crafter.Build-Project", "implementations/Crafter.Build-Shader", "implementations/Crafter.Build-Implementation", "implementations/Crafter.Build-Test"], + "libs": ["vulkan", "MachineIndependent", "OSDependent", "GenericCodeGen", "glslang", "glslang-default-resource-limits", "SPIRV", "tbb"] }, { "name": "executable", "extends": ["base"], "type":"executable", - "source_files": ["main"] + "implementations": ["implementations/main"] + }, + { + "name": "executable-debug", + "extends": ["executable"], + "debug": true }, { "name": "lib", @@ -22,26 +24,21 @@ "type":"library" }, { - "name": "debug", - "extends": ["executable"], - "optimization_level": "0", - "debug": true - }, - { - "name": "release", - "extends": ["executable"], - "optimization_level": "3" - }, - { - "name": "debug-lib", + "name": "lib-debug", "extends": ["lib"], - "optimization_level": "0", "debug": true - }, + } + ], + "tests": [ { - "name": "release-lib", - "extends": ["lib"], - "optimization_level": "3" + "name": "should-compile", + "implementations": ["tests/ShouldCompile/ShouldCompile"], + "dependencies": [ + { + "path":"./project.json", + "configuration":"lib" + } + ] } ] -} +} \ No newline at end of file diff --git a/Crafter.Build-Dependency.cpp b/tests/ShouldCompile/ShouldCompile.cpp similarity index 54% rename from Crafter.Build-Dependency.cpp rename to tests/ShouldCompile/ShouldCompile.cpp index 6a1b9ae..cab62ba 100644 --- a/Crafter.Build-Dependency.cpp +++ b/tests/ShouldCompile/ShouldCompile.cpp @@ -1,14 +1,13 @@ /* -Crafter.Build +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. +License version 3.0 as published by the Free Software Foundation; - 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. @@ -17,12 +16,14 @@ 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 */ +import Crafter.Build; +import std; +using namespace Crafter; -module; -#include -module Crafter.Build; -using namespace Crafter::Build; - -Dependency::Dependency(std::string path, std::string configuration, std::string commit, std::string branch): path(path), configuration(configuration), commit(commit), branch(branch) { - +extern "C" { + std::string* RunTest() { + return nullptr; + } } + +