rewrite
This commit is contained in:
parent
23fa8b98b0
commit
0eed272765
35 changed files with 1634 additions and 1507 deletions
25
.github/workflows/release.yaml
vendored
25
.github/workflows/release.yaml
vendored
|
|
@ -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 }}
|
|
||||||
|
|
@ -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 <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <array>
|
|
||||||
#include <string>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string_view>
|
|
||||||
#include <regex>
|
|
||||||
export module Crafter.Build:Bounce;
|
|
||||||
|
|
||||||
namespace Crafter::Build {
|
|
||||||
export 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<ClangError> RunClang(const std::string_view cmd) {
|
|
||||||
std::string result = RunCommand(cmd);
|
|
||||||
std::vector<ClangError> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <glslang/Public/ShaderLang.h>
|
|
||||||
module Crafter.Build;
|
|
||||||
using namespace Crafter::Build;
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
Configuration::Configuration(std::string name, std::string standard, std::vector<fs::path> sourceFiles, std::vector<fs::path> moduleFiles, std::string optimizationLevel, std::string buildDir, std::string outputDir, std::string type, std::string target, std::string march, std::vector<Dependency> dependencies, std::vector<fs::path> additionalFiles, std::vector<std::string> flags, bool debug, std::vector<std::string> libs, std::vector<std::string> lib_paths, std::vector<fs::path> c_files, std::vector<Shader> shaderFiles, std::vector<std::string> includeDirs, bool verbose, std::vector<Define> 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<std::string>();
|
|
||||||
for (auto& [key, val] : config.items())
|
|
||||||
{
|
|
||||||
if(key == "standard"){
|
|
||||||
standard = val.get<std::string>();
|
|
||||||
} else if(key == "target") {
|
|
||||||
target = val.get<std::string>();
|
|
||||||
} else if(key == "debug") {
|
|
||||||
debug = val.get<bool>();
|
|
||||||
} else if(key == "verbose") {
|
|
||||||
verbose = val.get<bool>();
|
|
||||||
} else if(key == "type") {
|
|
||||||
type = val.get<std::string>();
|
|
||||||
} else if(key == "march") {
|
|
||||||
march = val.get<std::string>();
|
|
||||||
} else if(key == "source_files") {
|
|
||||||
const std::vector<std::string> tempSourceFiles = val.get<std::vector<std::string>>();
|
|
||||||
sourceFiles = std::vector<fs::path>(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<std::string> tempSourceFiles = val.get<std::vector<std::string>>();
|
|
||||||
c_files = std::vector<fs::path>(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<std::vector<std::string>>();
|
|
||||||
} else if(key == "lib_paths") {
|
|
||||||
lib_paths = val.get<std::vector<std::string>>();
|
|
||||||
} else if(key == "include_dirs") {
|
|
||||||
includeDirs = val.get<std::vector<std::string>>();
|
|
||||||
} else if(key == "libs") {
|
|
||||||
libs = val.get<std::vector<std::string>>();
|
|
||||||
} else if(key == "module_files") {
|
|
||||||
const std::vector<std::string> tempModuleFiles = val.get<std::vector<std::string>>();
|
|
||||||
moduleFiles = std::vector<fs::path>(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<std::string>());
|
|
||||||
const std::filesystem::path fullFilePath = workingDir / filePath;
|
|
||||||
shaderFiles.emplace_back(fullFilePath, it["entrypoint"].get<std::string>(), static_cast<EShLanguage>(it["type"].get<std::uint32_t>()));
|
|
||||||
}
|
|
||||||
} else if(key == "defines") {
|
|
||||||
for (auto it : val) {
|
|
||||||
std::string name = it["name"].get<std::string>();
|
|
||||||
std::string value = it["value"].get<std::string>();
|
|
||||||
defines.emplace_back(name, value);
|
|
||||||
}
|
|
||||||
} else if(key == "additional_files") {
|
|
||||||
const std::vector<std::string> tempAdditionalFiles = val.get<std::vector<std::string>>();
|
|
||||||
additionalFiles = std::vector<fs::path>(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<std::string>();
|
|
||||||
} else if(key == "build_dir") {
|
|
||||||
const std::string tempBuildDir = val.get<std::string>();
|
|
||||||
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<std::string>();
|
|
||||||
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<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>(), commit, branch);
|
|
||||||
}
|
|
||||||
} else if(key != "extends") {
|
|
||||||
additionalProperties.insert({key, val});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "json.hpp"
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <format>
|
|
||||||
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<fs::path> sourceFiles;
|
|
||||||
std::vector<fs::path> c_files;
|
|
||||||
std::vector<fs::path> moduleFiles;
|
|
||||||
std::vector<Shader> shaderFiles;
|
|
||||||
std::vector<fs::path> additionalFiles;
|
|
||||||
std::vector<std::string> includeDirs;
|
|
||||||
std::vector<Define> defines;
|
|
||||||
std::string optimizationLevel;
|
|
||||||
std::string buildDir;
|
|
||||||
std::string outputDir;
|
|
||||||
std::string type;
|
|
||||||
std::string target;
|
|
||||||
std::string march;
|
|
||||||
bool debug;
|
|
||||||
std::vector<std::string> libs;
|
|
||||||
std::vector<std::string> lib_paths;
|
|
||||||
std::vector<Dependency> dependencies;
|
|
||||||
std::unordered_map<std::string, nlohmann::json> additionalProperties;
|
|
||||||
std::vector<std::string> flags;
|
|
||||||
bool verbose = false;
|
|
||||||
Configuration(std::string name, std::string standard, std::vector<fs::path> sourceFiles, std::vector<fs::path> moduleFiles, std::string optimizationLevel, std::string buildDir, std::string outputDir, std::string type, std::string target, std::string march, std::vector<Dependency> dependencies, std::vector<fs::path> additionalFiles, std::vector<std::string> flags, bool debug, std::vector<std::string> libs, std::vector<std::string> lib_paths, std::vector<fs::path> c_files, std::vector<Shader> shaderFiles, std::vector<std::string> includeDirs, bool verbose, std::vector<Define> defines);
|
|
||||||
Configuration(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -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 <string>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <regex>
|
|
||||||
#include <print>
|
|
||||||
#include <execution>
|
|
||||||
#include <tuple>
|
|
||||||
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<ClangError> ModulePartition::Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags) {
|
|
||||||
std::vector<ClangError> 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<ClangError> 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<ClangError> 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<ClangError> errors;
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
for(auto& [key, val] : partitions) {;
|
|
||||||
if(val.needsRecompiling) {
|
|
||||||
threads.emplace_back([&val, &clangDir, &config, &pcmDir, &target, &march, &flags, &errors, &errorMutex](){
|
|
||||||
std::vector<ClangError> 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<std::thread> threads2;
|
|
||||||
for(auto& [key, val] : partitions) {;
|
|
||||||
if(val.needsRecompiling) {
|
|
||||||
threads2.emplace_back([&val, &clangDir, &config, &pcmDir, &target, &march, &flags, &buildDir, &errors, &errorMutex](){
|
|
||||||
std::vector<ClangError> 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<ClangError> 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>, std::vector<ClangError>> 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<Module> 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<ClangError> 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<ClangError> 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};
|
|
||||||
}
|
|
||||||
|
|
@ -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 <string>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
#include <atomic>
|
|
||||||
#include <thread>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <tuple>
|
|
||||||
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<std::string> moduleDependencies;
|
|
||||||
std::vector<ModulePartition*> partitionDependants;
|
|
||||||
std::vector<std::string> partitionDependencies;
|
|
||||||
std::vector<ModulePartition*> partitionDependenciesP;
|
|
||||||
const std::string name;
|
|
||||||
std::atomic<bool>* compiled = new std::atomic<bool>(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<ClangError> Compile(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags);
|
|
||||||
std::vector<ClangError> 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<std::string, ModulePartition> 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<ClangError> 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<Module>, std::vector<ClangError>> 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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -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 <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <print>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include "json.hpp"
|
|
||||||
#include <filesystem>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <thread>
|
|
||||||
#include <glslang/SPIRV/GlslangToSpv.h>
|
|
||||||
#include <regex>
|
|
||||||
#include <mutex>
|
|
||||||
#include <tuple>
|
|
||||||
module Crafter.Build;
|
|
||||||
using namespace Crafter::Build;
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
Project::Project(std::string name, fs::path path, std::vector<Configuration> configurations) : name(name), path(path), configurations(configurations) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<Configuration&, std::vector<ClangError>> 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<Configuration&, std::vector<ClangError>> 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<Configuration&, std::vector<ClangError>> 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<ClangError> Project::Build(Configuration& configuration) {
|
|
||||||
return Build(configuration, configuration.outputDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ClangError> Project::Build(Configuration& config, fs::path outputDir) {
|
|
||||||
return Build(config, outputDir, outputDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ClangError> 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<std::thread> depThreads = std::vector<std::thread>(config.dependencies.size());
|
|
||||||
|
|
||||||
if(config.dependencies.size() > 0){
|
|
||||||
libs += std::format(" -L{}", pcmDir.generic_string());
|
|
||||||
}
|
|
||||||
std::unordered_set<std::string> 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<Configuration&, std::vector<ClangError>> 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<Configuration&, std::vector<ClangError>> 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<Module>, std::vector<ClangError>> 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<ClangError> errors = Source::GetSourceFiles(clangDir, config, pcmDir, target, march, flags, std::get<0>(modules), files, buildDir);
|
|
||||||
if(errors.size() > 0) {
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::thread> 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<ClangError> 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::string>();
|
|
||||||
std::vector<Configuration> 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);
|
|
||||||
}
|
|
||||||
|
|
@ -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 <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <tuple>
|
|
||||||
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<Configuration> configurations;
|
|
||||||
Project(std::string name, fs::path path, std::vector<Configuration> configurations);
|
|
||||||
std::tuple<Configuration&, std::vector<ClangError>> Build(std::string configuration);
|
|
||||||
std::tuple<Configuration&, std::vector<ClangError>> Build(std::string configuration, fs::path outputDir);
|
|
||||||
std::tuple<Configuration&, std::vector<ClangError>> Build(std::string configuration, fs::path outputDir, fs::path binDir);
|
|
||||||
std::vector<ClangError> Build(Configuration& configuration);
|
|
||||||
std::vector<ClangError> Build(Configuration& configuration, fs::path outputDir);
|
|
||||||
std::vector<ClangError> Build(Configuration& configuration, fs::path outputDir, fs::path binDir);
|
|
||||||
void SaveToJSON(fs::path path);
|
|
||||||
static Project LoadFromJSON(fs::path path);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -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 <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <glslang/SPIRV/GlslangToSpv.h>
|
|
||||||
#include <glslang/Public/ShaderLang.h>
|
|
||||||
#include <glslang/Public/ResourceLimits.h>
|
|
||||||
#include "DirStackFileIncluder.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iterator>
|
|
||||||
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<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("spirv");
|
|
||||||
std::ofstream file(outputDir/filename, std::ios::binary);
|
|
||||||
file.write(reinterpret_cast<const char*>(spirv.data()), spirv.size() * sizeof(std::uint32_t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <string>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <regex>
|
|
||||||
#include <print>
|
|
||||||
#include <mutex>
|
|
||||||
#include <execution>
|
|
||||||
module Crafter.Build;
|
|
||||||
using namespace Crafter::Build;
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
std::vector<ClangError> 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<Module>& modules, std::string& files, const fs::path& buildDir) {
|
|
||||||
std::string defines;
|
|
||||||
for(const Define& define : config.defines) {
|
|
||||||
defines+=define.ToString();
|
|
||||||
}
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
std::mutex errorMutex;
|
|
||||||
std::vector<ClangError> 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<ClangError> 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<Module>& 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;
|
|
||||||
}
|
|
||||||
|
|
@ -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 <string>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
#include <atomic>
|
|
||||||
#include <thread>
|
|
||||||
#include <condition_variable>
|
|
||||||
export module Crafter.Build:SourceFile;
|
|
||||||
import :Configuration;
|
|
||||||
import :ModuleFile;
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
namespace Crafter::Build {
|
|
||||||
class Source {
|
|
||||||
public:
|
|
||||||
static std::vector<ClangError> GetSourceFiles(std::string clangDir, const Configuration& config, fs::path pcmDir, std::string target, const std::string& march, const std::string& flags, const std::vector<Module>& modules, std::string& files, const fs::path& buildDir);
|
|
||||||
private:
|
|
||||||
static bool Check(const fs::path& path, const std::vector<Module>& modules, const Configuration& config, const fs::path& buildDir);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -7,11 +7,10 @@ This is a simple and easy to use C++ build system for Linux designed for use wit
|
||||||
## Prerequisites:
|
## Prerequisites:
|
||||||
```
|
```
|
||||||
lld
|
lld
|
||||||
clang 18>
|
clang
|
||||||
git
|
git
|
||||||
libpqxx
|
lldb
|
||||||
onetbb
|
onetbb
|
||||||
boost
|
|
||||||
glslang
|
glslang
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
96
build.sh
96
build.sh
|
|
@ -1,31 +1,79 @@
|
||||||
mkdir build
|
mkdir build
|
||||||
mkdir bin
|
mkdir bin
|
||||||
|
|
||||||
clang++ -std=c++26 Crafter.Build-Bounce.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Bounce.pcm
|
GCC_VERSION=$(g++ -dumpversion)
|
||||||
clang++ -std=c++26 Crafter.Build-Shader.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Shader.pcm
|
STD_HEADER="/usr/include/c++/$GCC_VERSION/bits/std.cc"
|
||||||
clang++ -std=c++26 Crafter.Build-Dependency.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Dependency.pcm
|
if [ ! -f "$STD_HEADER" ]; then
|
||||||
clang++ -std=c++26 Crafter.Build-Configuration.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Configuration.pcm
|
echo "Cannot find std.cc for GCC $GCC_VERSION"
|
||||||
clang++ -std=c++26 Crafter.Build-ModuleFile.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-ModuleFile.pcm
|
exit 1
|
||||||
clang++ -std=c++26 Crafter.Build-SourceFile.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-SourceFile.pcm
|
fi
|
||||||
clang++ -std=c++26 Crafter.Build-Project.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build-Project.pcm
|
cp "$STD_HEADER" ./build/std.cppm
|
||||||
clang++ -std=c++26 Crafter.Build.cppm --precompile -fprebuilt-module-path=./build -o ./build/Crafter.Build.pcm
|
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 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-FixedVector.pcm -o ./build/Crafter.Build-FixedVector.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 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Command.pcm -o ./build/Crafter.Build-Command.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 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Shader.pcm -o ./build/Crafter.Build-Shader.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 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Module.pcm -o ./build/Crafter.Build-Module.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 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Implementation.pcm -o ./build/Crafter.Build-Implementation.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 -O3 -fprebuilt-module-path=./build -c ./build/Crafter.Build-Configuration.pcm -o ./build/Crafter.Build-Configuration.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-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 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Command.cpp -o ./build/Crafter.Build-Command_impl.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 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Test.cpp -o ./build/Crafter.Build-Test_impl.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 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Implementation.cpp -o ./build/Crafter.Build-Implementation_impl.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 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Shader.cpp -o ./build/Crafter.Build-Shader_impl.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 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Module.cpp -o ./build/Crafter.Build-Module_impl.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 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Configuration.cpp -o ./build/Crafter.Build-Configuration_impl.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 -O3 -fprebuilt-module-path=./build -c ./implementations/Crafter.Build-Project.cpp -o ./build/Crafter.Build-Project_impl.o
|
||||||
clang++ -std=c++26 ./build/Crafter.Build.pcm -fprebuilt-module-path=./build -c -O3 -o ./build/Crafter.Build.o
|
clang++ -std=c++26 -O3 -fprebuilt-module-path=./build -c ./implementations/main.cpp -o ./build/main.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 -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
|
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
|
||||||
|
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
interfaces/Crafter.Build-Command.cppm
Normal file
42
interfaces/Crafter.Build-Command.cppm
Normal file
|
|
@ -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<CompileError> errors;
|
||||||
|
CompileException(std::vector<CompileError>&& 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);
|
||||||
|
}
|
||||||
72
interfaces/Crafter.Build-Configuration.cppm
Normal file
72
interfaces/Crafter.Build-Configuration.cppm
Normal file
|
|
@ -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<std::unique_ptr<Module>> interfaces;
|
||||||
|
std::vector<Implementation> implementations;
|
||||||
|
std::vector<Dependency> dependencies;
|
||||||
|
std::vector<Shader> shaders;
|
||||||
|
std::vector<fs::path> additionalFiles;
|
||||||
|
std::vector<Define> defines;
|
||||||
|
fs::path buildDir;
|
||||||
|
fs::path outputDir;
|
||||||
|
ConfigurationType type;
|
||||||
|
std::string target;
|
||||||
|
std::string march;
|
||||||
|
bool debug;
|
||||||
|
std::vector<std::string> 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
82
interfaces/Crafter.Build-FixedVector.cppm
Normal file
82
interfaces/Crafter.Build-FixedVector.cppm
Normal file
|
|
@ -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 <typename T>
|
||||||
|
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<T*>(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<T*>(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_; }
|
||||||
|
};
|
||||||
|
}
|
||||||
36
interfaces/Crafter.Build-Implementation.cppm
Normal file
36
interfaces/Crafter.Build-Implementation.cppm
Normal file
|
|
@ -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<Module*> moduleDependencies;
|
||||||
|
std::vector<ModulePartition*> 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
54
interfaces/Crafter.Build-Module.cppm
Normal file
54
interfaces/Crafter.Build-Module.cppm
Normal file
|
|
@ -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<Module*> moduleDependencies;
|
||||||
|
std::vector<ModulePartition*> partitionDependencies;
|
||||||
|
std::atomic<bool> 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<bool> compiled;
|
||||||
|
bool needsRecompiling;
|
||||||
|
bool checked = false;
|
||||||
|
std::vector<std::unique_ptr<ModulePartition>> 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<std::unique_ptr<ModulePartition>>&& partitions);
|
||||||
|
bool Check(const fs::path& pcmDir);
|
||||||
|
void Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir);
|
||||||
|
};
|
||||||
|
}
|
||||||
47
interfaces/Crafter.Build-Project.cppm
Normal file
47
interfaces/Crafter.Build-Project.cppm
Normal file
|
|
@ -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<Configuration> configurations;
|
||||||
|
std::vector<Test> tests;
|
||||||
|
Project(std::string&& name, fs::path&& path, std::vector<Configuration>&& 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<TestResult> RunTests();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
/*
|
/*
|
||||||
Crafter.Build
|
Crafter® Build
|
||||||
Copyright (C) 2025 Catcrafts®
|
Copyright (C) 2025 Catcrafts®
|
||||||
Catcrafts.net
|
Catcrafts.net
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
License as published by the Free Software Foundation; either
|
License version 3.0 as published by the Free Software Foundation;
|
||||||
version 3.0 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
|
@ -19,19 +18,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module;
|
module;
|
||||||
#include <string>
|
#include <glslang/SPIRV/GlslangToSpv.h>
|
||||||
#include <filesystem>
|
|
||||||
#include <glslang/Public/ShaderLang.h>
|
#include <glslang/Public/ShaderLang.h>
|
||||||
|
#include <glslang/Public/ResourceLimits.h>
|
||||||
|
#include "../lib/DirStackFileIncluder.h"
|
||||||
export module Crafter.Build:Shader;
|
export module Crafter.Build:Shader;
|
||||||
|
import std;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
export namespace Crafter::Build {
|
namespace Crafter {
|
||||||
class Shader {
|
export class Shader {
|
||||||
public:
|
public:
|
||||||
fs::path path;
|
fs::path path;
|
||||||
std::string entrypoint;
|
std::string entrypoint;
|
||||||
EShLanguage type;
|
EShLanguage type;
|
||||||
Shader(fs::path path, std::string entrypoint, EShLanguage type);
|
Shader(fs::path&& path, std::string&& entrypoint, EShLanguage type);
|
||||||
void Compile(fs::path outputDir);
|
bool Check(const fs::path& outputDir) const;
|
||||||
|
void Compile(const fs::path& outputDir) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
/*
|
/*
|
||||||
Crafter.Build
|
Crafter® Build
|
||||||
Copyright (C) 2025 Catcrafts®
|
Copyright (C) 2025 Catcrafts®
|
||||||
Catcrafts.net
|
Catcrafts.net
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
License as published by the Free Software Foundation; either
|
License version 3.0 as published by the Free Software Foundation;
|
||||||
version 3.0 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
|
@ -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
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module;
|
module;
|
||||||
#include <string>
|
#include "../lib/json.hpp"
|
||||||
export module Crafter.Build:Dependency;
|
export module Crafter.Build:Test;
|
||||||
|
import std;
|
||||||
|
import :Configuration;
|
||||||
|
|
||||||
export namespace Crafter::Build {
|
namespace fs = std::filesystem;
|
||||||
class Dependency {
|
|
||||||
public:
|
namespace Crafter {
|
||||||
std::string path;
|
export struct TestResult {
|
||||||
std::string configuration;
|
|
||||||
std::string commit;
|
|
||||||
std::string branch;
|
|
||||||
std::string name;
|
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
/*
|
/*
|
||||||
Crafter.Build
|
Crafter® Build
|
||||||
Copyright (C) 2025 Catcrafts®
|
Copyright (C) 2025 Catcrafts®
|
||||||
Catcrafts.net
|
Catcrafts.net
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
License as published by the Free Software Foundation; either
|
License version 3.0 as published by the Free Software Foundation;
|
||||||
version 3.0 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
|
@ -19,10 +18,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export module Crafter.Build;
|
export module Crafter.Build;
|
||||||
export import :Dependency;
|
export import :Command;
|
||||||
export import :Project;
|
export import :Project;
|
||||||
|
export import :Module;
|
||||||
export import :Configuration;
|
export import :Configuration;
|
||||||
export import :ModuleFile;
|
|
||||||
export import :SourceFile;
|
|
||||||
export import :Shader;
|
export import :Shader;
|
||||||
export import :Bounce;
|
export import :Implementation;
|
||||||
|
export import :FixedVector;
|
||||||
|
export import :Test;
|
||||||
92
main.cpp
92
main.cpp
|
|
@ -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 <cstdint>
|
|
||||||
#include <print>
|
|
||||||
#include <tuple>
|
|
||||||
#include <iostream>
|
|
||||||
#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<ClangError> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
project.json
45
project.json
|
|
@ -3,18 +3,20 @@
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "base",
|
"name": "base",
|
||||||
"standard": "c++26",
|
"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"],
|
||||||
"source_files": ["Crafter.Build-Configuration", "Crafter.Build-Project", "Crafter.Build-Dependency", "Crafter.Build-ModuleFile", "Crafter.Build-Shader", "Crafter.Build-SourceFile"],
|
"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"],
|
||||||
"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"],
|
"libs": ["vulkan", "MachineIndependent", "OSDependent", "GenericCodeGen", "glslang", "glslang-default-resource-limits", "SPIRV", "tbb"]
|
||||||
"build_dir": "build",
|
|
||||||
"output_dir": "bin",
|
|
||||||
"libs": ["vulkan", "MachineIndependent", "OSDependent", "GenericCodeGen", "glslang", "glslang-default-resource-limits", "SPIRV", "SPVRemapper", "tbb"]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "executable",
|
"name": "executable",
|
||||||
"extends": ["base"],
|
"extends": ["base"],
|
||||||
"type":"executable",
|
"type":"executable",
|
||||||
"source_files": ["main"]
|
"implementations": ["implementations/main"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "executable-debug",
|
||||||
|
"extends": ["executable"],
|
||||||
|
"debug": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lib",
|
"name": "lib",
|
||||||
|
|
@ -22,26 +24,21 @@
|
||||||
"type":"library"
|
"type":"library"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "debug",
|
"name": "lib-debug",
|
||||||
"extends": ["executable"],
|
|
||||||
"optimization_level": "0",
|
|
||||||
"debug": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "release",
|
|
||||||
"extends": ["executable"],
|
|
||||||
"optimization_level": "3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "debug-lib",
|
|
||||||
"extends": ["lib"],
|
"extends": ["lib"],
|
||||||
"optimization_level": "0",
|
|
||||||
"debug": true
|
"debug": true
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"tests": [
|
||||||
{
|
{
|
||||||
"name": "release-lib",
|
"name": "should-compile",
|
||||||
"extends": ["lib"],
|
"implementations": ["tests/ShouldCompile/ShouldCompile"],
|
||||||
"optimization_level": "3"
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"path":"./project.json",
|
||||||
|
"configuration":"lib"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
/*
|
/*
|
||||||
Crafter.Build
|
Crafter® Build
|
||||||
Copyright (C) 2025 Catcrafts®
|
Copyright (C) 2025 Catcrafts®
|
||||||
Catcrafts.net
|
Catcrafts.net
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
License as published by the Free Software Foundation; either
|
License version 3.0 as published by the Free Software Foundation;
|
||||||
version 3.0 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
|
@ -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
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
import Crafter.Build;
|
||||||
|
import std;
|
||||||
|
using namespace Crafter;
|
||||||
|
|
||||||
module;
|
extern "C" {
|
||||||
#include <string>
|
std::string* RunTest() {
|
||||||
module Crafter.Build;
|
return nullptr;
|
||||||
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) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue