rewrite
This commit is contained in:
parent
23fa8b98b0
commit
0eed272765
35 changed files with 1634 additions and 1507 deletions
90
implementations/Crafter.Build-Command.cpp
Normal file
90
implementations/Crafter.Build-Command.cpp
Normal file
|
|
@ -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 <stdio.h>
|
||||
module Crafter.Build:Command_impl;
|
||||
import :Command;
|
||||
import std;
|
||||
|
||||
|
||||
namespace Crafter {
|
||||
std::string RunCommand(const std::string_view cmd) {
|
||||
std::array<char, 128> 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<CompileError>&& 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<CompileError> 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);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
241
implementations/Crafter.Build-Configuration.cpp
Normal file
241
implementations/Crafter.Build-Configuration.cpp
Normal file
|
|
@ -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 <glslang/Public/ShaderLang.h>
|
||||
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<std::string> extends = config["extends"].get<std::vector<std::string>>();
|
||||
for(const std::string& extendName : extends) {
|
||||
for (auto it : configs) {
|
||||
if(it["name"].get<std::string>() == extendName) {
|
||||
SetDataFromJson(configs, it, workingDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(config.contains("name")) {
|
||||
name = config["name"].get<std::string>();
|
||||
} else {
|
||||
throw std::runtime_error("Invalid config json, name field is missing");
|
||||
}
|
||||
if(config.contains("standard")) {
|
||||
standard = config["standard"].get<std::string>();
|
||||
}
|
||||
if(config.contains("target")) {
|
||||
target = config["target"].get<std::string>();
|
||||
}
|
||||
if(config.contains("debug")) {
|
||||
debug = config["debug"].get<bool>();
|
||||
}
|
||||
if(config.contains("type")) {
|
||||
std::string typeString = config["type"].get<std::string>();
|
||||
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<std::string>();
|
||||
}
|
||||
if(config.contains("libs")) {
|
||||
for (auto it : config["libs"]) {
|
||||
libs.push_back(it.get<std::string>());
|
||||
}
|
||||
}
|
||||
if(config.contains("interfaces")) {
|
||||
const std::vector<std::string> tempModuleFiles = config["interfaces"].get<std::vector<std::string>>();
|
||||
std::vector<std::tuple<fs::path, std::string, ModulePartition*, Module*>> tempModulePaths = std::vector<std::tuple<fs::path, std::string, ModulePartition*, Module*>>(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<fs::path, std::string, ModulePartition*, Module*>& 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<Module>(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<Module>& 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<ModulePartition> partition = std::make_unique<ModulePartition>(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<fs::path, std::string, ModulePartition*, Module*>& 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<ModulePartition>& 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<std::string> tempFiles = config["implementations"].get<std::vector<std::string>>();
|
||||
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<Module>& 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<ModulePartition>& 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<Module>& 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<std::string>(), it["entrypoint"].get<std::string>(), static_cast<EShLanguage>(it["type"].get<std::uint32_t>()));
|
||||
}
|
||||
}
|
||||
if(config.contains("defines")) {
|
||||
for (auto it : config["defines"]) {
|
||||
defines.emplace_back(it["name"].get<std::string>(), it["value"].get<std::string>());
|
||||
}
|
||||
}
|
||||
if(config.contains("additional_files")) {
|
||||
const std::vector<std::string> tempAdditionalFiles = config["additional_files"].get<std::vector<std::string>>();
|
||||
for (const std::string& file : tempAdditionalFiles) {
|
||||
additionalFiles.push_back( workingDir / file);
|
||||
}
|
||||
}
|
||||
if(config.contains("build_dir")) {
|
||||
buildDir = workingDir / config["build_dir"].get<std::string>();
|
||||
}
|
||||
if(config.contains("output_dir")) {
|
||||
outputDir = workingDir / config["output_dir"].get<std::string>();
|
||||
}
|
||||
if(config.contains("dependencies")) {
|
||||
for (auto it : config["dependencies"]) {
|
||||
std::string commit;
|
||||
std::string branch;
|
||||
if(it.contains("commit")){
|
||||
commit = it["commit"].get<std::string>();
|
||||
}
|
||||
if(it.contains("branch")){
|
||||
branch = it["branch"].get<std::string>();
|
||||
}
|
||||
dependencies.emplace_back(it["path"].get<std::string>(), it["configuration"].get<std::string>(), std::move(commit), std::move(branch));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
implementations/Crafter.Build-Implementation.cpp
Normal file
61
implementations/Crafter.Build-Implementation.cpp
Normal file
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
139
implementations/Crafter.Build-Module.cpp
Normal file
139
implementations/Crafter.Build-Module.cpp
Normal file
|
|
@ -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<std::unique_ptr<ModulePartition>>&& 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<ModulePartition>& 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<ModulePartition>& 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<std::thread> threads;
|
||||
for(std::unique_ptr<ModulePartition>& 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();
|
||||
}
|
||||
}
|
||||
384
implementations/Crafter.Build-Project.cpp
Normal file
384
implementations/Crafter.Build-Project.cpp
Normal file
|
|
@ -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 <dlfcn.h>
|
||||
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<Configuration>&& 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<std::thread> 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<std::string> depLibSet;
|
||||
std::vector<std::thread> depThreads = std::vector<std::thread>(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<Module>& 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<ModulePartition>& 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::string>();
|
||||
std::vector<Configuration> configurations;
|
||||
nlohmann::json configs = data["configurations"];
|
||||
std::vector<Test> 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<TestResult> Project::RunTests() {
|
||||
std::vector<TestResult> 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;
|
||||
}
|
||||
100
implementations/Crafter.Build-Shader.cpp
Normal file
100
implementations/Crafter.Build-Shader.cpp
Normal file
|
|
@ -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 <glslang/SPIRV/GlslangToSpv.h>
|
||||
#include <glslang/Public/ShaderLang.h>
|
||||
#include <glslang/Public/ResourceLimits.h>
|
||||
#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<EShMessages>(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<const char *>(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<std::uint32_t> 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<const char*>(spirv.data()), spirv.size() * sizeof(std::uint32_t));
|
||||
}
|
||||
}
|
||||
34
implementations/Crafter.Build-Test.cpp
Normal file
34
implementations/Crafter.Build-Test.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
105
implementations/main.cpp
Normal file
105
implementations/main.cpp
Normal file
|
|
@ -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 <stdlib.h>
|
||||
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<TestResult> 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue