V2 progress
This commit is contained in:
parent
d2f5863552
commit
5e1fcd8590
9837 changed files with 790 additions and 1708518 deletions
362
implementations/Crafter.Build-Clang.cpp
Normal file
362
implementations/Crafter.Build-Clang.cpp
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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:Clang_impl;
|
||||
import std;
|
||||
import :Clang;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
BuildResult Build(const Configuration& config, std::unordered_set<fs::path>& depSet, std::mutex& depMutex) {
|
||||
fs::path pwd = std::filesystem::path cwd = std::filesystem::current_path();
|
||||
fs::path buildDir = pwd/"build";
|
||||
fs::path outputDir = pwd/"bin";
|
||||
|
||||
if (!fs::exists(buildDir)) {
|
||||
fs::create_directories(buildDir);
|
||||
}
|
||||
|
||||
if (!fs::exists(outputDir)) {
|
||||
fs::create_directories(outputDir);
|
||||
}
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(config.shaders.size() + 1 + config.interfaces.size() + config.implementations.size());
|
||||
|
||||
std::string buildError;
|
||||
std::atomic<bool> buildCancelled{false};
|
||||
for (const Shader& shader : config.shaders) {
|
||||
if (shader.Check(outputDir)) continue;
|
||||
threads.emplace_back([&shader, &outputDir, &buildError, &buildCancelled]() {
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||
|
||||
std::string result = shader.Compile(outputDir);
|
||||
if (result.empty()) return;
|
||||
|
||||
bool expected = false;
|
||||
if (buildCancelled.compare_exchange_strong(expected, true)) {
|
||||
buildError = std::move(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
threads.emplace_back([&config, &outputDir, &buildCancelled, &buildError]() {
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||
try {
|
||||
for (const fs::path& additionalFile : config.files) {
|
||||
fs::path destination = outputDir / additionalFile.filename();
|
||||
|
||||
if (fs::is_directory(additionalFile)) {
|
||||
for (const auto& entry : fs::recursive_directory_iterator(additionalFile)) {
|
||||
|
||||
const fs::path& sourcePath = entry.path();
|
||||
|
||||
// Compute relative path inside the directory
|
||||
fs::path relativePath = fs::relative(sourcePath, additionalFile);
|
||||
fs::path destPath = destination / relativePath;
|
||||
|
||||
if (entry.is_directory()) {
|
||||
// Ensure directory exists in destination
|
||||
if (!fs::exists(destPath)) {
|
||||
fs::create_directories(destPath);
|
||||
}
|
||||
} else if (entry.is_regular_file()) {
|
||||
// Ensure parent directory exists
|
||||
fs::create_directories(destPath.parent_path());
|
||||
|
||||
if (!fs::exists(destPath)) {
|
||||
fs::copy_file(sourcePath, destPath);
|
||||
}
|
||||
else if (fs::last_write_time(sourcePath) > fs::last_write_time(destPath)) {
|
||||
fs::copy_file(sourcePath, destPath, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Handle regular file
|
||||
if (!fs::exists(destination)) {
|
||||
fs::copy_file(additionalFile, destination);
|
||||
}
|
||||
else if (fs::last_write_time(additionalFile) > fs::last_write_time(destination)) {
|
||||
fs::copy_file(additionalFile, destination, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
bool expected = false;
|
||||
if (buildCancelled.compare_exchange_strong(expected, true)) {
|
||||
buildError = e.what();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fs::path stdPcmDir = GetCacheDir()/config.target/"-"/config.march;
|
||||
|
||||
if (!fs::exists(stdPcmDir)) {
|
||||
fs::create_directories(stdPcmDir);
|
||||
}
|
||||
|
||||
std::string stdPcmResult = BuildStdPcm(config, stdPcmDir/"std.pcm");
|
||||
if(!stdPcmResult.empty()) {
|
||||
return {result, false, {}};
|
||||
}
|
||||
|
||||
fs::path pcmDir;
|
||||
|
||||
if(config.type != ConfigurationType::Executable) {
|
||||
pcmDir = outputDir;
|
||||
} else {
|
||||
pcmDir = buildDir;
|
||||
}
|
||||
|
||||
std::string command = std::format("{} --target={} -march={} -mtune={} -std=c++26 -D CRAFTER_BUILD_CONFIGURATION_TARGET=\\\"{}\\\" -D CRAFTER_BUILD_CONFIGURATION_TARGET_{} -fprebuilt-module-path={} -fprebuilt-module-path={}", GetBaseCommand(), config.target, config.march, config.mtune, editedTarget, editedTarget, stdPcm.parent_path().string(), stdPcmDir);
|
||||
|
||||
if(config.type == CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY) {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
command += " -fPIC -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
||||
#endif
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
||||
#endif
|
||||
} else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
||||
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_LIBRARY";
|
||||
} else {
|
||||
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_EXECUTABLE";
|
||||
}
|
||||
|
||||
std::string files;
|
||||
std::unordered_set<std::string> libSet;
|
||||
std::mutex fileMutex;
|
||||
std::vector<std::thread> depThreads;
|
||||
depThreads.reserve(config.dependencies);
|
||||
std::atomic<bool> repack(false);
|
||||
|
||||
for(const Dependency& dep : config.dependencies) {
|
||||
for (const auto& entry : fs::recursive_directory_iterator(dep.path)) {
|
||||
if (entry.is_directory() && entry.path().filename() == "include") {
|
||||
command += " -I" + entry.path().string();
|
||||
}
|
||||
}
|
||||
|
||||
command += std::format(" -I{} -fprebuilt-module-path={}", dep.path.string(), (dep.path/"bin"/(config.target+config.march)).string());
|
||||
|
||||
depThreads.emplace_back([&](){
|
||||
if (!buildCancelled.load(std::memory_order_relaxed)) {
|
||||
depMutex.lock();
|
||||
if(!depSet.contains(dep.path)) {
|
||||
depSet.insert(dep.path);
|
||||
depMutex.unlock();
|
||||
BuildResult result = Build(dep.path, depSet, depMutex);
|
||||
fileMutex.lock();
|
||||
libSet.merge(result.libs);
|
||||
fileMutex.unlock();
|
||||
if (buildCancelled.compare_exchange_strong(false, true)) {
|
||||
buildError = std::move(result.result);
|
||||
}
|
||||
if(result.repack) {
|
||||
repack = true;
|
||||
}
|
||||
} else {
|
||||
depMutex.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for(const Define& define : config.defines) {
|
||||
if(define.value.empty()) {
|
||||
command += std::format(" -D {}", define.name);
|
||||
} else {
|
||||
command += std::format(" -D {}={}", define.name, define.value);
|
||||
}
|
||||
}
|
||||
|
||||
std::string cmakeBuildType;
|
||||
|
||||
if(config.debug) {
|
||||
cmakeBuildType = "Debug";
|
||||
command += " -g -D CRAFTER_BUILD_CONFIGURATION_DEBUG";
|
||||
} else {
|
||||
cmakeBuildType = "Release";
|
||||
command += " -O3";
|
||||
}
|
||||
|
||||
for (const fs::path& cFile : config.c_files) {
|
||||
files += std::format(" {}_source.o ", (buildDir / cFile.filename()).string());
|
||||
const std::string objPath = (buildDir / cFile.filename()).string() + "_source.o";
|
||||
const std::string srcPath = cFile.string() + ".c";
|
||||
if (!fs::exists(objPath) || fs::last_write_time(srcPath) > fs::last_write_time(objPath)) {
|
||||
threads.emplace_back([&cFile, &buildDir, &buildError, &buildCancelled]() {
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||
|
||||
std::string result = RunCommand(std::format("clang {}.c --target={} -march={} -mtune={} -O3 -c -o {}_source.o", cFile.string(), config.target, config.march, config.mtune, (buildDir / cFile.filename()).string()));
|
||||
if (result.empty()) return;
|
||||
|
||||
bool expected = false;
|
||||
if (buildCancelled.compare_exchange_strong(expected, true)) {
|
||||
buildError = std::move(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const fs::path& cFile : config.cuda) {
|
||||
files += std::format(" {}_source.o ", (buildDir / cFile.filename()).string());
|
||||
const std::string objPath = (buildDir / cFile.filename()).string() + "_source.o";
|
||||
const std::string srcPath = cFile.string() + ".cu";
|
||||
if (!fs::exists(objPath) || fs::last_write_time(srcPath) > fs::last_write_time(objPath)) {
|
||||
threads.emplace_back([&cFile, &buildDir, &buildError, &buildCancelled]() {
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||
|
||||
std::string result = RunCommand(std::format("nvcc {}.cu -c -o {}_source.o -O3 -arch=sm_89", cFile.string(), (buildDir / cFile.filename()).string()));
|
||||
if (result.empty()) return;
|
||||
|
||||
bool expected = false;
|
||||
if (buildCancelled.compare_exchange_strong(expected, true)) {
|
||||
buildError = std::move(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for(std::thread& thread : depThreads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
if(buildCancelled.load()) {
|
||||
return {buildError, false, {}};
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Module>> interfaces;
|
||||
std::vector<std::tuple<fs::path, std::string, ModulePartition*, Module*>> tempModulePaths = std::vector<std::tuple<fs::path, std::string, ModulePartition*, Module*>>(config.interfaces.size());
|
||||
for(std::uint16_t i = 0; i < config.interfaces.size(); i++){
|
||||
const std::filesystem::path file = workingDir / (config.interfaces[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(std::uint16_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;
|
||||
}
|
||||
}
|
||||
|
||||
for(Module& interface : interfaces) {
|
||||
if(config.interfaces[i]->Check(pcmDir)) {
|
||||
threads.emplace_back(&Module::Compile, interface, command, pcmDir, buildDir, buildCancelled, buildError);
|
||||
buildResult.repack = true;
|
||||
}
|
||||
files += std::format(" {}/{}.o", buildDir.string(), interface.path.filename().string());
|
||||
for(std::unique_ptr<ModulePartition>& part : interface.partitions) {
|
||||
files += std::format(" {}/{}.o", buildDir.string(), part->path.filename().string());
|
||||
}
|
||||
}
|
||||
|
||||
for(Implementation& interface : implementations) {
|
||||
if(config.implementations[i].Check(buildDir, pcmDir)) {
|
||||
buildResult.repack = true;
|
||||
threads.emplace_back(&Implementation::Compile, &config.implementations[i], command, buildDir, buildCancelled, buildError);
|
||||
}
|
||||
files += std::format(" {}/{}_impl.o", buildDir.string(), config.implementations[i].path.filename().string());
|
||||
}
|
||||
|
||||
for(std::thread& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
if(buildCancelled.load()) {
|
||||
return {buildError, false, {}};
|
||||
}
|
||||
|
||||
if(buildResult.repack) {
|
||||
if(config.type == CRAFTER_CONFIGURATION_TYPE_EXECUTABLE) {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
buildResult.result = RunCommand(std::format("{}{} -o {} -fuse-ld=lld ", command, files, (binDir/outputName).string()));
|
||||
#endif
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
system(std::format("copy \"%LIBCXX_DIR%\\lib\\c++.dll\" \"{}\\c++.dll\"", binDir.string()).c_str());
|
||||
buildResult.result = RunCommand(std::format("{}{} -o {}.exe -fuse-ld=lld -L %LIBCXX_DIR%\\lib -lc++ -nostdinc++ -nostdlib++", command, files, (binDir/outputName).string()));
|
||||
#endif
|
||||
} else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
buildResult.result = RunCommand(std::format("ar r {}.a {}", (binDir/fs::path(std::string("lib")+outputName)).string(), files));
|
||||
#endif
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
buildResult.result = RunCommand(std::format("llvm-lib.exe {} /OUT:{}.lib", files, (binDir/fs::path(outputName)).string()));
|
||||
#endif
|
||||
} else {
|
||||
buildResult.result = RunCommand(std::format("{}{} -shared -o {}.so -Wl,-rpath,'$ORIGIN' -fuse-ld=lld", command, files, (binDir/(std::string("lib")+outputName)).string()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return buildResult;
|
||||
}
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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;
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
module Crafter.Build:Command_impl;
|
||||
import :Command;
|
||||
import :Project;
|
||||
import std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
namespace Crafter {
|
||||
std::string RunCommand(const std::string_view cmd);
|
||||
std::filesystem::path GetPath();
|
||||
|
||||
void BuildWasmStdPcm(const Project& project, const Configuration& config) {
|
||||
// fs::path exeDir = GetPath();
|
||||
// fs::create_directories(exeDir/config.target);
|
||||
|
||||
// const std::string stdPcm = std::format("{}/wasi-sysroot-28.0/share/libc++/v1/std.pcm", exeDir.string());
|
||||
// fs::path stdCc = fs::path(std::format("{}/wasi-sysroot-28.0/share/libc++/v1/std.cppm", exeDir.string()));
|
||||
// if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) {
|
||||
// std::string result = RunCommand(std::format("clang++ -fno-exceptions --target=wasm32-wasi -nodefaultlibs --sysroot={}/wasi-sysroot-28.0 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier -fno-c++-static-destructors --precompile {} -o {}", exeDir.string(), stdCc.string(), stdPcm));
|
||||
// if(result != "") {
|
||||
// throw std::runtime_error(result);
|
||||
// }
|
||||
// }
|
||||
|
||||
// const fs::path indexPath = std::format("{}/index.html", exeDir.string());
|
||||
// const fs::path indexDstPath = project.binDir/"index.html";
|
||||
// if(!fs::exists(indexDstPath)) {
|
||||
// fs::copy(indexPath, indexDstPath);
|
||||
// } else if(fs::last_write_time(indexDstPath) < fs::last_write_time(indexPath)) {
|
||||
// fs::remove(indexDstPath);
|
||||
// fs::copy(indexPath, indexDstPath);
|
||||
// }
|
||||
|
||||
// const fs::path runtimePath = std::format("{}/runtime.js", exeDir.string());
|
||||
// const fs::path runtimeDstPath = project.binDir/"runtime.js";
|
||||
// if(!fs::exists(runtimeDstPath)) {
|
||||
// fs::copy(runtimePath, runtimeDstPath);
|
||||
// } else if(fs::last_write_time(runtimeDstPath) < fs::last_write_time(runtimePath)) {
|
||||
// fs::remove(runtimeDstPath);
|
||||
// fs::copy(runtimePath, runtimeDstPath);
|
||||
// }
|
||||
}
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
std::string RunCommand(const std::string_view cmd) {
|
||||
std::array<char, 128> buffer;
|
||||
std::string result;
|
||||
|
||||
// Use cmd.exe to interpret redirection
|
||||
std::string with = "cmd /C \"" + std::string(cmd) + " 2>&1\"";
|
||||
|
||||
FILE* pipe = _popen(with.c_str(), "r");
|
||||
if (!pipe) {
|
||||
throw std::runtime_error("_popen() failed!");
|
||||
}
|
||||
|
||||
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe) != nullptr) {
|
||||
result += buffer.data();
|
||||
}
|
||||
|
||||
_pclose(pipe);
|
||||
return result;
|
||||
}
|
||||
|
||||
void RunCommandIgnore(const std::string_view cmd) {
|
||||
std::string with = "cmd /C \"" + std::string(cmd) + " > NUL 2>&1\"";
|
||||
|
||||
FILE* pipe = _popen(with.c_str(), "r");
|
||||
if (!pipe) {
|
||||
throw std::runtime_error("_popen() failed!");
|
||||
}
|
||||
|
||||
_pclose(pipe);
|
||||
}
|
||||
|
||||
std::filesystem::path GetPath() {
|
||||
char path[MAX_PATH];
|
||||
DWORD length = GetModuleFileNameA(NULL, path, MAX_PATH);
|
||||
if (length == 0) {
|
||||
throw std::runtime_error("Failed to get executable path");
|
||||
}
|
||||
path[length] = '\0';
|
||||
return fs::path(path).parent_path().parent_path();
|
||||
}
|
||||
|
||||
void BuildGnuStdPcm(const Project& project, const Configuration& config) {
|
||||
throw std::runtime_error("target x86_64-pc-linux-gnu is not supported");
|
||||
}
|
||||
|
||||
void BuildMingwStdPcm(const Project& project, const Configuration& config) {
|
||||
throw std::runtime_error("target x86_64-w64-mingw32 is not supported");
|
||||
}
|
||||
|
||||
void BuildMsvcStdPcm(const Project& project, const Configuration& config) {
|
||||
fs::path exeDir = GetPath();
|
||||
fs::create_directories(exeDir/(config.target+config.march));
|
||||
|
||||
std::string libcxx = std::getenv("LIBCXX_DIR");
|
||||
|
||||
std::string stdPcm = std::format("{}\\{}{}\\std.pcm", exeDir.string(), config.target, config.march);
|
||||
std::string stdcppm = std::format("{}\\modules\\c++\\v1\\std.cppm", libcxx);
|
||||
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdcppm)) {
|
||||
std::string result = RunCommand(std::format("clang++ --target={} -march={} -mtune={} -isystem %LIBCXX_DIR%\\include\\c++\\v1 -nostdinc++ -nostdlib++ -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile %LIBCXX_DIR%\\modules\\c++\\v1\\std.cppm -o {}", config.target, config.march, config.mtune, stdPcm));
|
||||
if(result != "") {
|
||||
std::cout << result << std::endl;
|
||||
throw std::runtime_error(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::filesystem::path GetPath() {
|
||||
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';
|
||||
return fs::path(path).parent_path().parent_path();
|
||||
}
|
||||
|
||||
void BuildGnuStdPcm(const Project& project, const Configuration& config) {
|
||||
fs::path exeDir = GetPath();
|
||||
fs::create_directories(exeDir/(config.target+config.march));
|
||||
const std::string stdPcm = std::format("{}/{}{}/std.pcm", exeDir.string(), config.target, config.march);
|
||||
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time("/usr/share/libc++/v1/std.cppm")) {
|
||||
std::string result = RunCommand(std::format("clang++ --target={} -std=c++26 -stdlib=libc++ -march={} -mtune={} -O3 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile /usr/share/libc++/v1/std.cppm -o {}", config.target, config.march, config.mtune, stdPcm));
|
||||
if(result != "") {
|
||||
throw std::runtime_error(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildMingwStdPcm(const Project& project, const Configuration& config) {
|
||||
fs::path exeDir = GetPath();
|
||||
fs::create_directories(exeDir/(config.target+config.march));
|
||||
|
||||
const std::string stdPcm = std::format("{}/{}{}/std.pcm", exeDir.string(), config.target, config.march);
|
||||
std::vector<std::string> folders;
|
||||
|
||||
// Iterate through the directory and collect all subdirectories
|
||||
for (const auto& entry : fs::directory_iterator("/usr/x86_64-w64-mingw32/include/c++")) {
|
||||
if (entry.is_directory()) {
|
||||
folders.push_back(entry.path().filename().string());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the folders by version in descending order
|
||||
std::sort(folders.begin(), folders.end(), [](const std::string& a, const std::string& b) {
|
||||
return std::lexicographical_compare(b.begin(), b.end(), a.begin(), a.end());
|
||||
});
|
||||
|
||||
std::string mingWversion = folders.front();
|
||||
|
||||
fs::path stdCc = fs::path(std::format("/usr/x86_64-w64-mingw32/include/c++/{}/bits/std.cc", mingWversion));
|
||||
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) {
|
||||
std::string result = RunCommand(std::format("cp {} {}/{}{}/std.cppm\nclang++ --target={} -march={} -mtune={} -O3 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {}/{}{}/std.cppm -o {}", stdCc.string(), exeDir.string(), config.target, config.march, config.target, config.march, config.mtune, exeDir.string(), config.target, config.march, stdPcm));
|
||||
if(result != "") {
|
||||
throw std::runtime_error(result);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Iterate over the source directory
|
||||
for (const auto& entry : fs::directory_iterator("/usr/x86_64-w64-mingw32/bin/")) {
|
||||
// Check if the file is a regular file and ends with ".dll"
|
||||
if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") {
|
||||
// Construct the destination file path
|
||||
fs::path dest_file = project.binDir / config.name / entry.path().filename();
|
||||
|
||||
// Check if the destination file exists and if it is older than the source file
|
||||
if (!fs::exists(dest_file) || fs::last_write_time(entry.path()) > fs::last_write_time(dest_file)) {
|
||||
// Copy the file if it doesn't exist or is older
|
||||
fs::copy(entry.path(), dest_file, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void BuildMsvcStdPcm(const Project& project, const Configuration& config) {
|
||||
throw std::runtime_error("target x86_64-pc-windows-msvc is not supported");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// 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 BuildStdPcm(const Project& project, const Configuration& config) {
|
||||
if(config.target == "x86_64-pc-linux-gnu") {
|
||||
BuildGnuStdPcm(project, config);
|
||||
} else if(config.target == "x86_64-w64-mingw32") {
|
||||
BuildMingwStdPcm(project, config);
|
||||
} else if(config.target == "x86_64-pc-windows-msvc") {
|
||||
BuildMsvcStdPcm(project, config);
|
||||
} else if(config.target == "wasm32-wasi") {
|
||||
BuildWasmStdPcm(project, config);
|
||||
} else {
|
||||
std::cout << config.target << std::endl;
|
||||
throw std::runtime_error(std::format("Unkown target: {}", config.target));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RunClang(const std::string_view cmd) {
|
||||
// std::string result = RunCommand(cmd);
|
||||
// // 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);
|
||||
// //}
|
||||
// }
|
||||
std::cout << cmd << std::endl;
|
||||
std::string result = RunCommand(cmd);
|
||||
std::cout << result << std::endl;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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>
|
||||
#include <stdlib.h>
|
||||
module Crafter.Build:Configuration_impl;
|
||||
import :Configuration;
|
||||
import std;
|
||||
import :Project;
|
||||
//import :Dependency;
|
||||
import :Shader;
|
||||
import :Module;
|
||||
import :Command;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::unordered_set<std::string> checkedDeps;
|
||||
|
||||
namespace Crafter {
|
||||
Configuration::Configuration(std::string&& name) : name(std::move(name)) {
|
||||
|
||||
}
|
||||
Configuration::Configuration(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir, const Project& project) : type(CRAFTER_CONFIGURATION_TYPE_EXECUTABLE), debug(false) {
|
||||
SetDataFromJson(configs, config, workingDir, project);
|
||||
}
|
||||
|
||||
void Configuration::SetDataFromJson(const nlohmann::json& configs, const nlohmann::json& config, fs::path workingDir, const Project& project) {
|
||||
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, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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>();
|
||||
} else {
|
||||
standard = "c++26";
|
||||
}
|
||||
if(config.contains("target")) {
|
||||
target = config["target"].get<std::string>();
|
||||
} else {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
target = RunCommand("clang -print-target-triple");
|
||||
#endif
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
target = "x86_64-pc-windows-msvc ";
|
||||
//target = RunCommand(std::format("{} && clang-cl.exe -print-target-triple", vsVars));
|
||||
#endif
|
||||
target.pop_back();
|
||||
}
|
||||
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>();
|
||||
} else {
|
||||
march = "native";
|
||||
}
|
||||
if(config.contains("mtune")) {
|
||||
mtune = config["mtune"].get<std::string>();
|
||||
} else if(config.contains("march")) {
|
||||
mtune = march;
|
||||
} else {
|
||||
mtune = "native";
|
||||
}
|
||||
if(config.contains("libs")) {
|
||||
for (auto it : config["libs"]) {
|
||||
libs.push_back(it.get<std::string>());
|
||||
}
|
||||
}
|
||||
if(config.contains("include_dirs")) {
|
||||
for (auto it : config["include_dirs"]) {
|
||||
includeDirs.push_back(it.get<std::string>());
|
||||
}
|
||||
}
|
||||
if(config.contains("lib_dirs")) {
|
||||
for (auto it : config["lib_dirs"]) {
|
||||
libDirs.push_back(it.get<std::string>());
|
||||
}
|
||||
}
|
||||
if(config.contains("cuda")) {
|
||||
for (auto it : config["cuda"]) {
|
||||
cuda.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("c_files")) {
|
||||
for (auto it : config["c_files"]) {
|
||||
c_files.push_back(workingDir / it.get<std::string>());
|
||||
}
|
||||
}
|
||||
if(config.contains("defines")) {
|
||||
for (auto it : config["defines"]) {
|
||||
if(it.contains("value")) {
|
||||
defines.emplace_back(it["name"].get<std::string>(), it["value"].get<std::string>());
|
||||
} else {
|
||||
defines.emplace_back(it["name"].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("dependencies")) {
|
||||
for (auto it : config["dependencies"]) {
|
||||
fs::path path = it["path"].get<std::string>();
|
||||
if(path.string().ends_with(".git")) {
|
||||
fs::path name = path.filename();
|
||||
name.replace_extension();
|
||||
std::string depFolder = name.string();
|
||||
|
||||
// if (it.contains("commit")) {
|
||||
// depFolder += it["commit"].get<std::string>();
|
||||
// } else if (it.contains("branch")) {
|
||||
// depFolder += it["branch"].get<std::string>();
|
||||
// }
|
||||
|
||||
const fs::path cacheDir = GetPath() / "cloneCache";
|
||||
|
||||
if (!fs::exists(cacheDir)) {
|
||||
fs::create_directories(cacheDir);
|
||||
}
|
||||
|
||||
if (!fs::exists(cacheDir/depFolder)) {
|
||||
std::string cloneCommand = std::format("cd {} && git clone {} && cd {}", cacheDir.string(), path.string(), depFolder);
|
||||
|
||||
if (it.contains("branch")) {
|
||||
cloneCommand += std::format(" && git switch {}", it["branch"].get<std::string>());
|
||||
} else if (it.contains("commit")) {
|
||||
cloneCommand += std::format(" && git checkout {}", it["commit"].get<std::string>());
|
||||
}
|
||||
|
||||
// Execute the clone command
|
||||
system(cloneCommand.c_str());
|
||||
} else if (!it.contains("commit") && !checkedDeps.contains((cacheDir/depFolder).string())) {
|
||||
checkedDeps.insert((cacheDir/depFolder).string());
|
||||
system(std::format("cd {} && git pull", (cacheDir/depFolder).string()).c_str());
|
||||
}
|
||||
|
||||
// Update path to the project.json of the dependency
|
||||
path = cacheDir / depFolder / "project.json";
|
||||
}
|
||||
if(it.contains("type")) {
|
||||
std::string type = it["type"].get<std::string>();
|
||||
if(type == "cmake") {
|
||||
std::string options;
|
||||
if(it.contains("options")) {
|
||||
options = it["options"].get<std::string>();
|
||||
}
|
||||
cmakeDeps.emplace_back(path.parent_path(), options);
|
||||
continue;
|
||||
} else if(type == "include") {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::string configName = it["configuration"].get<std::string>();
|
||||
|
||||
// Create a Project object for the dependency
|
||||
std::unique_ptr<Project> depProject = std::make_unique<Project>(std::move(path), configName);
|
||||
|
||||
// Add the dependency to the list
|
||||
dependencies.emplace_back(std::move(depProject), depProject->configurations[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,23 +45,24 @@ namespace Crafter {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
void Implementation::Compile(const std::string_view clang, const fs::path& buildDir, std::string& result) const {
|
||||
void Implementation::Compile(const std::string_view clang, const fs::path& buildDir, std::atomic<bool>& buildCancelled, std::string& buildError) const {
|
||||
for(ModulePartition* dependency : partitionDependencies) {
|
||||
if(!dependency->compiled.load()) {
|
||||
dependency->compiled.wait(CRAFTER_COMPILE_STATUS_WAITING);
|
||||
if(dependency->compiled.load() == CRAFTER_COMPILE_STATUS_ERROR) {
|
||||
return;
|
||||
}
|
||||
dependency->compiled.wait(true);
|
||||
}
|
||||
}
|
||||
for(Module* dependency : moduleDependencies) {
|
||||
if(!dependency->compiled.load()) {
|
||||
dependency->compiled.wait(CRAFTER_COMPILE_STATUS_WAITING);
|
||||
if(dependency->compiled.load() == CRAFTER_COMPILE_STATUS_ERROR) {
|
||||
return;
|
||||
}
|
||||
dependency->compiled.wait(true);
|
||||
}
|
||||
}
|
||||
result = RunClang(std::format("{} {}.cpp -c -o {}_impl.o", clang, path.string(), (buildDir/path.filename()).string()));
|
||||
|
||||
if (!buildCancelled.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string result = RunCommand(std::format("{} {}.cpp -c -o {}_impl.o", clang, path.string(), (buildDir/path.filename()).string()));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
166
implementations/Crafter.Build-Interface.cpp
Normal file
166
implementations/Crafter.Build-Interface.cpp
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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:Interface_impl;
|
||||
import std;
|
||||
import :Interface;
|
||||
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), checked(false) {}
|
||||
|
||||
bool ModulePartition::Check(const fs::path& pcmDir) {
|
||||
if(!checked) {
|
||||
checked = true;
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Module* dependency : moduleDependencies) {
|
||||
if(dependency->Check(pcmDir)) {
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
needsRecompiling = false;
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_COMPLETED);
|
||||
return false;
|
||||
} else {
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return needsRecompiling;
|
||||
}
|
||||
}
|
||||
|
||||
void ModulePartition::Compile(const std::string_view clang, const fs::path& pcmDir, std::atomic<bool>& buildCancelled, std::string& buildError) {
|
||||
for(ModulePartition* dependency : partitionDependencies) {
|
||||
if(!dependency->compiled.load()) {
|
||||
dependency->compiled.wait(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) {
|
||||
compiled.store(true);
|
||||
compiled.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string result = RunCommand(std::format("{} {}.cppm --precompile -o {}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
|
||||
if (!result.empty()) {
|
||||
if (buildCancelled.compare_exchange_strong(false, true)) {
|
||||
buildError = std::move(result);
|
||||
compiled.store(true);
|
||||
compiled.notify_all();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
compiled.store(true);
|
||||
compiled.notify_all();
|
||||
|
||||
result = RunClang(std::format("{} -Wno-unused-command-line-argument {}.pcm -c -o {}.o", clang, (pcmDir/path.filename()).string(), (buildDir/path.filename()).string()));
|
||||
|
||||
if (!result.empty()) {
|
||||
if (buildCancelled.compare_exchange_strong(false, true)) {
|
||||
buildError = std::move(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Module::Module(std::string&& name, fs::path&& path) : name(std::move(name)), path(std::move(path)), compiled(false), checked(false) {}
|
||||
|
||||
bool Module::Check(const fs::path& pcmDir) {
|
||||
if(!checked) {
|
||||
checked = true;
|
||||
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")) {
|
||||
bool depCheck = false;
|
||||
for(std::unique_ptr<ModulePartition>& partition : partitions) {
|
||||
if(partition->Check(pcmDir)) {
|
||||
depCheck = true;
|
||||
}
|
||||
}
|
||||
if(depCheck) {
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
} else {
|
||||
needsRecompiling = false;
|
||||
compiled.store(true);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for(std::unique_ptr<ModulePartition>& partition : partitions) {
|
||||
partition->Check(pcmDir);
|
||||
}
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return needsRecompiling;
|
||||
}
|
||||
}
|
||||
|
||||
void Module::Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir, std::atomic<bool>& buildCancelled, std::string& buildError) {
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(interface.partitions.size());
|
||||
for(std::unique_ptr<ModulePartition>& part : interface.partitions) {
|
||||
if(part.needsRecompiling) {
|
||||
threads.emplace_back(&ModulePartition::Compile, part.get(), clang, pcmDir, buildDir, buildCancelled, buildError);
|
||||
}
|
||||
}
|
||||
|
||||
for(std::thread& thread : threads){
|
||||
thread.join();
|
||||
}
|
||||
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) {
|
||||
compiled.store(true);
|
||||
compiled.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string result = RunCommand(std::format("{} {}.cppm --precompile -o {}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
|
||||
if (!result.empty()) {
|
||||
if (buildCancelled.compare_exchange_strong(false, true)) {
|
||||
buildError = std::move(result);
|
||||
compiled.store(true);
|
||||
compiled.notify_all();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
compiled.store(true);
|
||||
compiled.notify_all();
|
||||
|
||||
result = RunCommand(std::format("{} -Wno-unused-command-line-argument {}.pcm -c -o {}.o", clang, (pcmDir/path.filename()).string(), (buildDir/path.filename()).string()));
|
||||
|
||||
if (!result.empty()) {
|
||||
if (buildCancelled.compare_exchange_strong(false, true)) {
|
||||
buildError = std::move(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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(CRAFTER_COMPILE_STATUS_WAITING), checked(false) {}
|
||||
|
||||
bool ModulePartition::Check(const fs::path& pcmDir) {
|
||||
if(!checked) {
|
||||
checked = true;
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Module* dependency : moduleDependencies) {
|
||||
if(dependency->Check(pcmDir)) {
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
needsRecompiling = false;
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_COMPLETED);
|
||||
return false;
|
||||
} else {
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return needsRecompiling;
|
||||
}
|
||||
}
|
||||
|
||||
void ModulePartition::Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir, std::string& result) {
|
||||
for(ModulePartition* dependency : partitionDependencies) {
|
||||
if(!dependency->compiled.load()) {
|
||||
//std::cout << std::format("{} is waiting on {} {}", name, dependency->name, dependency->needsRecompiling) << std::endl;
|
||||
dependency->compiled.wait(CRAFTER_COMPILE_STATUS_WAITING);
|
||||
if(dependency->compiled.load() == CRAFTER_COMPILE_STATUS_ERROR) {
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_ERROR);
|
||||
compiled.notify_all();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result += RunClang(std::format("{} {}.cppm --precompile -o {}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
if(result.empty()) {
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_COMPLETED);
|
||||
compiled.notify_all();
|
||||
} else {
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_ERROR);
|
||||
compiled.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void ModulePartition::CompileSource(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir, std::string& result) {
|
||||
this->compiled.wait(CRAFTER_COMPILE_STATUS_WAITING);
|
||||
if(this->compiled.load() != CRAFTER_COMPILE_STATUS_ERROR) {
|
||||
result += RunClang(std::format("{} -Wno-unused-command-line-argument {}.pcm -c -o {}.o", clang, (pcmDir/path.filename()).string(), (buildDir/path.filename()).string()));
|
||||
}
|
||||
}
|
||||
|
||||
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(CRAFTER_COMPILE_STATUS_WAITING), checked(false) {}
|
||||
|
||||
Module::Module(std::string&& name, fs::path&& path) : name(std::move(name)), path(std::move(path)), compiled(CRAFTER_COMPILE_STATUS_WAITING), checked(false) {}
|
||||
|
||||
Module::Module(fs::path&& path) : path(std::move(path)), compiled(CRAFTER_COMPILE_STATUS_WAITING), checked(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) {
|
||||
checked = true;
|
||||
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")) {
|
||||
bool depCheck = false;
|
||||
for(std::unique_ptr<ModulePartition>& partition : partitions) {
|
||||
if(partition->Check(pcmDir)) {
|
||||
depCheck = true;
|
||||
}
|
||||
}
|
||||
if(depCheck) {
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
} else {
|
||||
needsRecompiling = false;
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_COMPLETED);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for(std::unique_ptr<ModulePartition>& partition : partitions) {
|
||||
partition->Check(pcmDir);
|
||||
}
|
||||
needsRecompiling = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return needsRecompiling;
|
||||
}
|
||||
}
|
||||
|
||||
void Module::Compile(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir, std::string& result) {
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<std::string> results(partitions.size());
|
||||
for(std::uint_fast32_t i = 0; i < partitions.size(); i++) {
|
||||
if( partitions[i]->needsRecompiling) {
|
||||
threads.emplace_back(&ModulePartition::Compile, partitions[i].get(), clang, pcmDir, buildDir, std::ref(results[i]));
|
||||
}
|
||||
}
|
||||
|
||||
for(std::thread& thread : threads){
|
||||
thread.join();
|
||||
}
|
||||
|
||||
for(const std::string& result2 : results) {
|
||||
result += result2;
|
||||
}
|
||||
|
||||
if(result.empty()) {
|
||||
result += RunClang(std::format("{} {}.cppm --precompile -o {}.pcm", clang, path.string(), (pcmDir/path.filename()).string()));
|
||||
if(result.empty()) {
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_COMPLETED);
|
||||
compiled.notify_all();
|
||||
} else {
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_ERROR);
|
||||
compiled.notify_all();
|
||||
}
|
||||
} else {
|
||||
compiled.store(CRAFTER_COMPILE_STATUS_ERROR);
|
||||
compiled.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void Module::CompileSource(const std::string_view clang, const fs::path& pcmDir, const fs::path& buildDir, std::string& result) {
|
||||
this->compiled.wait(CRAFTER_COMPILE_STATUS_WAITING);
|
||||
if(this->compiled.load() != CRAFTER_COMPILE_STATUS_ERROR) {
|
||||
result += RunClang(std::format("{} -Wno-unused-command-line-argument {}.pcm -c -o {}.o", clang, (pcmDir/path.filename()).string(), (buildDir/path.filename()).string()));
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<std::string> results(partitions.size());
|
||||
for(std::uint_fast32_t i = 0; i < partitions.size(); i++) {
|
||||
if( partitions[i]->needsRecompiling) {
|
||||
threads.emplace_back(&ModulePartition::CompileSource, partitions[i].get(), clang, pcmDir, buildDir, std::ref(results[i]));
|
||||
}
|
||||
}
|
||||
|
||||
for(std::thread& thread : threads){
|
||||
thread.join();
|
||||
}
|
||||
|
||||
for(const std::string& result2 : results) {
|
||||
result += result2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
167
implementations/Crafter.Build-Platform.cpp
Normal file
167
implementations/Crafter.Build-Platform.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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:Platform_impl;
|
||||
import std;
|
||||
import :Platform;
|
||||
import :Clang;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
std::string RunCommand(const std::string_view cmd) {
|
||||
std::array<char, 128> buffer;
|
||||
std::string result;
|
||||
|
||||
// Use cmd.exe to interpret redirection
|
||||
std::string with = "cmd /C \"" + std::string(cmd) + " 2>&1\"";
|
||||
|
||||
FILE* pipe = _popen(with.c_str(), "r");
|
||||
if (!pipe) {
|
||||
throw std::runtime_error("_popen() failed!");
|
||||
}
|
||||
|
||||
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe) != nullptr) {
|
||||
result += buffer.data();
|
||||
}
|
||||
|
||||
_pclose(pipe);
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::path BuildStdPcm(Configuration& config, fs::path stdPcm) {
|
||||
std::string libcxx = std::getenv("LIBCXX_DIR");
|
||||
std::string stdPcm = std::format("{}\\{}{}\\std.pcm", exeDir.string(), config.target, config.march);
|
||||
std::string stdcppm = std::format("{}\\modules\\c++\\v1\\std.cppm", libcxx);
|
||||
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdcppm)) {
|
||||
return RunCommand(std::format("clang++ --target={} -march={} -mtune={} -isystem %LIBCXX_DIR%\\include\\c++\\v1 -nostdinc++ -nostdlib++ -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile %LIBCXX_DIR%\\modules\\c++\\v1\\std.cppm -o {}", config.target, config.march, config.mtune, stdPcm));
|
||||
}
|
||||
}
|
||||
|
||||
fs::path GetCacheDir() {
|
||||
if (const char* local = std::getenv("LOCALAPPDATA")) {
|
||||
return fs::path(local) / "crafter.build";
|
||||
}
|
||||
|
||||
throw std::runtime_error("LOCALAPPDATA not set");
|
||||
}
|
||||
|
||||
std::string GetBaseCommand(Configuration& config) {
|
||||
return std::format("clang++ -nostdinc++ -nostdlib++ -isystem %LIBCXX_DIR%\\include\\c++\\v1");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fs::path BuildStdPcm(Configuration& config, fs::path stdPcm) {
|
||||
if(config.target == "x86_64-w64-mingw32") {
|
||||
std::vector<std::string> folders;
|
||||
|
||||
// Iterate through the directory and collect all subdirectories
|
||||
for (const auto& entry : fs::directory_iterator("/usr/x86_64-w64-mingw32/include/c++")) {
|
||||
if (entry.is_directory()) {
|
||||
folders.push_back(entry.path().filename().string());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the folders by version in descending order
|
||||
std::sort(folders.begin(), folders.end(), [](const std::string& a, const std::string& b) {
|
||||
return std::lexicographical_compare(b.begin(), b.end(), a.begin(), a.end());
|
||||
});
|
||||
|
||||
std::string mingWversion = folders.front();
|
||||
|
||||
fs::path stdCc = fs::path(std::format("/usr/x86_64-w64-mingw32/include/c++/{}/bits/std.cc", mingWversion));
|
||||
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) {
|
||||
std::string result = RunCommand(std::format("cp {} {}/{}{}/std.cppm\nclang++ --target={} -march={} -mtune={} -O3 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {}/{}{}/std.cppm -o {}", stdCc.string(), exeDir.string(), config.target, config.march, config.target, config.march, config.mtune, exeDir.string(), config.target, config.march, stdPcm));
|
||||
if(!result.empty()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Iterate over the source directory
|
||||
for (const auto& entry : fs::directory_iterator("/usr/x86_64-w64-mingw32/bin/")) {
|
||||
// Check if the file is a regular file and ends with ".dll"
|
||||
if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") {
|
||||
// Construct the destination file path
|
||||
fs::path dest_file = project.binDir / config.name / entry.path().filename();
|
||||
|
||||
// Check if the destination file exists and if it is older than the source file
|
||||
if (!fs::exists(dest_file) || fs::last_write_time(entry.path()) > fs::last_write_time(dest_file)) {
|
||||
// Copy the file if it doesn't exist or is older
|
||||
fs::copy(entry.path(), dest_file, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
return e.what();
|
||||
}
|
||||
} else {
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time("/usr/share/libc++/v1/std.cppm")) {
|
||||
return RunCommand(std::format("clang++ --target={} -std=c++26 -stdlib=libc++ -march={} -mtune={} -O3 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile /usr/share/libc++/v1/std.cppm -o {}", config.target, config.march, config.mtune, stdPcm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs::path GetCacheDir() {
|
||||
if (const char* xdg = std::getenv("XDG_CACHE_HOME"); xdg && *xdg) {
|
||||
return fs::path(xdg) / "crafter.build";
|
||||
}
|
||||
|
||||
if (const char* home = std::getenv("HOME")) {
|
||||
return fs::path(home) / ".cache" / "crafter.build";
|
||||
}
|
||||
|
||||
throw std::runtime_error("Neither XDG_CACHE_HOME nor HOME set");
|
||||
}
|
||||
|
||||
std::string GetBaseCommand(Configuration& config) {
|
||||
std::string stdlib;
|
||||
if(config.target == "x86_64-w64-mingw32") {
|
||||
stdlib = "";
|
||||
} else {
|
||||
stdlib = "-stdlib=libc++";
|
||||
}
|
||||
std::string command = std::format("clang++ {}", stdlib);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,560 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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"
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
module Crafter.Build:Project_impl;
|
||||
import :Project;
|
||||
import std;
|
||||
import :Configuration;
|
||||
import :Command;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Crafter {
|
||||
Project::Project(fs::path&& path) : path(std::move(path)) {
|
||||
if (!fs::exists(this->path)) {
|
||||
throw std::runtime_error(std::format("Project file: {} not found.", this->path.generic_string()));
|
||||
}
|
||||
|
||||
std::ifstream f(this->path);
|
||||
nlohmann::json data;
|
||||
try {
|
||||
data = nlohmann::json::parse(f);
|
||||
} catch(std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
name = data["name"].get<std::string>();
|
||||
|
||||
this->path.remove_filename();
|
||||
|
||||
nlohmann::json configs = data["configurations"];
|
||||
nlohmann::json testJson = data["tests"];
|
||||
|
||||
if(data.contains("bin_dir")) {
|
||||
binDir = this->path/data["bin_dir"].get<std::string>();
|
||||
} else {
|
||||
binDir = this->path/"bin";
|
||||
}
|
||||
if(data.contains("build_dir")) {
|
||||
buildDir = this->path/data["build_dir"].get<std::string>();
|
||||
} else {
|
||||
buildDir = this->path/"build";
|
||||
}
|
||||
|
||||
if (!fs::exists(binDir)) {
|
||||
fs::create_directories(binDir);
|
||||
}
|
||||
|
||||
if (!fs::exists(buildDir)) {
|
||||
fs::create_directories(buildDir);
|
||||
}
|
||||
|
||||
for (nlohmann::json::iterator it = configs.begin(); it != configs.end(); ++it) {
|
||||
configurations.emplace_back(configs, (*it), this->path, *this);
|
||||
}
|
||||
|
||||
for (nlohmann::json::iterator it = testJson.begin(); it != testJson.end(); ++it) {
|
||||
tests.emplace_back(configs, (*it), this->path, *this);
|
||||
}
|
||||
|
||||
}
|
||||
Project::Project(fs::path&& path, const std::string_view config) : path(std::move(path)) {
|
||||
if (!fs::exists(this->path)) {
|
||||
throw std::runtime_error(std::format("Project file: {} not found.", this->path.generic_string()));
|
||||
}
|
||||
|
||||
std::ifstream f(this->path);
|
||||
nlohmann::json data = nlohmann::json::parse(f);
|
||||
name = data["name"].get<std::string>();
|
||||
|
||||
this->path.remove_filename();
|
||||
|
||||
nlohmann::json configs = data["configurations"];
|
||||
|
||||
if(data.contains("bin_dir")) {
|
||||
binDir = data["bin_dir"].get<std::string>();
|
||||
} else {
|
||||
binDir = "bin";
|
||||
}
|
||||
if(data.contains("build_dir")) {
|
||||
buildDir = data["build_dir"].get<std::string>();
|
||||
} else {
|
||||
buildDir = "build";
|
||||
}
|
||||
|
||||
if (!fs::exists(binDir)) {
|
||||
fs::create_directories(binDir);
|
||||
}
|
||||
|
||||
if (!fs::exists(buildDir)) {
|
||||
fs::create_directories(buildDir);
|
||||
}
|
||||
|
||||
for (nlohmann::json::iterator it = configs.begin(); it != configs.end(); ++it) {
|
||||
if(it->contains("name")) {
|
||||
if((*it)["name"].get<std::string>() == config) {
|
||||
configurations.emplace_back(configs, (*it), this->path, *this);
|
||||
goto errorSkip;
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Invalid config json, name field is missing");
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error(std::format("Config {} not found, requested as dependency", config));
|
||||
|
||||
errorSkip:;
|
||||
}
|
||||
Project::Project(std::string&& name, fs::path&& path, std::vector<Configuration>&& configurations) : name(std::move(name)), path(std::move(path)), configurations(std::move(configurations)) {}
|
||||
Project::Project(std::string&& name, fs::path&& path, std::vector<Configuration>&& configurations, fs::path&& binDir, fs::path&& buildDir) : name(std::move(name)), path(std::move(path)), configurations(std::move(configurations)), binDir(std::move(binDir)), buildDir(std::move(buildDir)) {}
|
||||
|
||||
|
||||
std::tuple<Configuration&, BuildResult> Project::Build(std::string_view configuration) {
|
||||
for(Configuration& config : configurations) {
|
||||
if(config.name == configuration){
|
||||
return {config, Build(config)};
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(std::format("Configuration: {} not found.", configuration));
|
||||
}
|
||||
std::tuple<Configuration&, BuildResult> Project::Build(std::string_view configuration, const fs::path& binDir, const fs::path& outputDir, const fs::path& buildDir, std::string outputName) {
|
||||
for(Configuration& config : configurations) {
|
||||
if(config.name == configuration){
|
||||
return {config, Build(config)};
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(std::format("Configuration: {} not found.", configuration));
|
||||
}
|
||||
BuildResult Project::Build(Configuration& config) const {
|
||||
return Build(config, binDir/config.name, binDir/config.name, buildDir/config.name, name);
|
||||
}
|
||||
|
||||
void AddLibsRecursive(std::string& libsString, std::unordered_set<std::string> depLibSet, Configuration& depConfig) {
|
||||
for(const std::string& lib2 : depConfig.libs) {
|
||||
if (depLibSet.insert(lib2).second) {
|
||||
libsString += std::format(" -l{}", lib2);
|
||||
}
|
||||
}
|
||||
for(const std::tuple<std::shared_ptr<Project>, Configuration&>& dep2 : depConfig.dependencies) {
|
||||
std::string outputLib = std::get<0>(dep2)->name+std::get<1>(dep2).name;
|
||||
if (depLibSet.insert(outputLib).second) {
|
||||
libsString += std::format(" -l{}", outputLib);
|
||||
}
|
||||
AddLibsRecursive(libsString, depLibSet, std::get<1>(dep2));
|
||||
}
|
||||
}
|
||||
|
||||
BuildResult Project::Build(Configuration& config, const fs::path& binDir, const fs::path& outputDir, const fs::path& buildDir, std::string outputName) const {
|
||||
BuildResult buildResult;
|
||||
if (!fs::exists(binDir)) {
|
||||
fs::create_directories(binDir);
|
||||
}
|
||||
|
||||
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, outputDir);
|
||||
}
|
||||
}
|
||||
|
||||
std::thread fileThread([&config, &outputDir](){
|
||||
for (const fs::path& additionalFile : config.additionalFiles) {
|
||||
fs::path destination = outputDir / additionalFile.filename();
|
||||
|
||||
if (fs::is_directory(additionalFile)) {
|
||||
|
||||
for (const auto& entry : fs::recursive_directory_iterator(additionalFile)) {
|
||||
|
||||
const fs::path& sourcePath = entry.path();
|
||||
|
||||
// Compute relative path inside the directory
|
||||
fs::path relativePath = fs::relative(sourcePath, additionalFile);
|
||||
fs::path destPath = destination / relativePath;
|
||||
|
||||
if (entry.is_directory()) {
|
||||
// Ensure directory exists in destination
|
||||
if (!fs::exists(destPath)) {
|
||||
fs::create_directories(destPath);
|
||||
}
|
||||
} else if (entry.is_regular_file()) {
|
||||
// Ensure parent directory exists
|
||||
fs::create_directories(destPath.parent_path());
|
||||
|
||||
if (!fs::exists(destPath)) {
|
||||
fs::copy_file(sourcePath, destPath);
|
||||
}
|
||||
else if (fs::last_write_time(sourcePath) > fs::last_write_time(destPath)) {
|
||||
fs::copy_file(sourcePath, destPath, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Handle regular file
|
||||
if (!fs::exists(destination)) {
|
||||
fs::copy_file(additionalFile, destination);
|
||||
}
|
||||
else if (fs::last_write_time(additionalFile) > fs::last_write_time(destination)) {
|
||||
fs::copy_file(additionalFile, destination, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fs::path exeDir = GetPath();
|
||||
|
||||
BuildStdPcm(*this, config);
|
||||
|
||||
std::string editedTarget = config.target;
|
||||
std::replace(editedTarget.begin(), editedTarget.end(), '-', '_');
|
||||
|
||||
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
std::string stdlib = "-stdlib=libc++";
|
||||
if(config.target == "x86_64-w64-mingw32") {
|
||||
stdlib = "";
|
||||
}
|
||||
std::string command = std::format("clang++ {} --target={} -march={} -mtune={} -std={} -D CRAFTER_BUILD_CONFIGURATION_TARGET=\\\"{}\\\" -D CRAFTER_BUILD_CONFIGURATION_TARGET_{} -fprebuilt-module-path={}", stdlib, config.target, config.march, config.mtune, config.standard, editedTarget, editedTarget, (exeDir/(config.target+config.march)).string());
|
||||
#endif
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
std::string command = std::format("clang++ -nostdinc++ -nostdlib++ -isystem %LIBCXX_DIR%\\include\\c++\\v1 --target={} -march={} -mtune={} -std={} -D CRAFTER_BUILD_CONFIGURATION_TARGET=\\\"{}\\\" -D CRAFTER_BUILD_CONFIGURATION_TARGET_{} -fprebuilt-module-path={}", config.target, config.march, config.mtune, config.standard, editedTarget, editedTarget, (exeDir/(config.target+config.march)).string());
|
||||
#endif
|
||||
|
||||
for (const auto& entry : fs::recursive_directory_iterator(buildDir)) {
|
||||
if (entry.is_directory() && entry.path().filename() == "include") {
|
||||
command += " -I" + entry.path().string() + " ";
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::recursive_directory_iterator(exeDir / "cloneCache")) {
|
||||
if (entry.is_directory() && entry.path().filename() == "include") {
|
||||
command += " -I" + entry.path().string() + " ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(buildDir)) {
|
||||
if (entry.is_directory()) {
|
||||
command += " -I" + entry.path().string() + " ";
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(exeDir / "cloneCache")) {
|
||||
if (entry.is_directory()) {
|
||||
command += " -I" + entry.path().string() + " ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(config.target == "wasm32-wasi") {
|
||||
command += std::format(" --sysroot={} -fno-exceptions -fno-c++-static-destructors", (exeDir/"wasi-sysroot-28.0").string());
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(const Define& define : config.defines) {
|
||||
if(define.value.empty()) {
|
||||
command += std::format(" -D {}", define.name);
|
||||
} else {
|
||||
command += std::format(" -D {}={}", define.name, define.value);
|
||||
}
|
||||
}
|
||||
|
||||
fs::path pcmDir;
|
||||
|
||||
if(config.type == CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY) {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
command += " -fPIC -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
||||
#endif
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
||||
#endif
|
||||
pcmDir = binDir;
|
||||
} else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
||||
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_LIBRARY";
|
||||
pcmDir = binDir;
|
||||
} else {
|
||||
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_EXECUTABLE";
|
||||
pcmDir = buildDir;
|
||||
}
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
command += " -Wno-deprecated-declarations";
|
||||
#endif
|
||||
|
||||
command += std::format(" -fprebuilt-module-path={}", pcmDir.string());
|
||||
|
||||
std::string cmakeBuildType;
|
||||
|
||||
if(config.debug) {
|
||||
cmakeBuildType = "Debug";
|
||||
command += " -g -D CRAFTER_BUILD_CONFIGURATION_DEBUG";
|
||||
} else {
|
||||
cmakeBuildType = "Release";
|
||||
command += " -O3";
|
||||
}
|
||||
|
||||
for(const std::string& dir : config.includeDirs) {
|
||||
command += std::format(" -I{}", dir);
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> depLibSet;
|
||||
std::vector<std::thread> depThreads = std::vector<std::thread>(config.dependencies.size());
|
||||
std::mutex libMutex;
|
||||
std::string files;
|
||||
std::string libsString;
|
||||
|
||||
std::vector<std::string> resultsDep(config.dependencies.size());
|
||||
|
||||
for(std::uint_fast32_t i = 0; i < depThreads.size(); i++) {
|
||||
depThreads[i] = std::thread([i, &config, &libMutex, &depLibSet, &buildDir, &pcmDir, &libsString, &binDir, this, &buildResult, &resultsDep](){
|
||||
for(Configuration& config2 : std::get<0>(config.dependencies[i])->configurations) {
|
||||
config2.target = config.target;
|
||||
config2.march = config.march;
|
||||
}
|
||||
std::string outputLib = std::get<0>(config.dependencies[i])->name+std::get<1>(config.dependencies[i]).name;
|
||||
BuildResult depResult = std::get<0>(config.dependencies[i])->Build(std::get<1>(config.dependencies[i]), pcmDir, binDir, buildDir/std::get<0>(config.dependencies[i])->name/std::get<0>(config.dependencies[i])->buildDir, outputLib);
|
||||
libMutex.lock();
|
||||
if(depResult.repack) {
|
||||
buildResult.repack = true;
|
||||
}
|
||||
resultsDep[i] = depResult.errors;
|
||||
|
||||
if (depLibSet.insert(outputLib).second) {
|
||||
libsString+=std::format(" -l{}", outputLib);
|
||||
}
|
||||
AddLibsRecursive(libsString, depLibSet, std::get<1>(config.dependencies[i]));
|
||||
libMutex.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
for(const CmakeDep& cmake : config.cmakeDeps) {
|
||||
depThreads.emplace_back([&cmake, &cmakeBuildType, &pcmDir](){
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
system(std::format("cd {} && cmake -B build -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=\"-stdlib=libc++\" -DCMAKE_EXE_LINKER_FLAGS=\"-stdlib=libc++\" -DCMAKE_SHARED_LINKER_FLAGS=\"-stdlib=libc++\" -DCMAKE_BUILD_TYPE={} -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY={} -DCMAKE_LIBRARY_OUTPUT_DIRECTORY={} {} && cmake --build build --config {}", cmake.path.string(), cmakeBuildType, pcmDir.string(), pcmDir.string(), cmake.options, cmakeBuildType).c_str());
|
||||
#endif
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
system(std::format("cd {} && cmake -G Ninja -B build -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=\"-nostdinc++ -nostdlib++ -isystem %LIBCXX_DIR%\\include\\c++\\v1\" -DCMAKE_EXE_LINKER_FLAGS=\"-nostdinc++ -nostdlib++ -L %LIBCXX_DIR%\\lib -lc++\" -DCMAKE_SHARED_LINKER_FLAGS=\"-nostdinc++ -nostdlib++ -L %LIBCXX_DIR%\\lib -lc++\" -DCMAKE_BUILD_TYPE={} -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY={} -DCMAKE_LIBRARY_OUTPUT_DIRECTORY={} {} && cmake --build build --config {}", cmake.path.string(), cmakeBuildType, pcmDir.string(), pcmDir.string(), cmake.options, cmakeBuildType).c_str());
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
for(std::thread& thread : depThreads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
for(const std::string& result2 : resultsDep) {
|
||||
buildResult.errors += result2;
|
||||
}
|
||||
|
||||
if(!buildResult.errors.empty()) {
|
||||
fileThread.join();
|
||||
return buildResult;
|
||||
}
|
||||
|
||||
|
||||
for(const fs::path& cFile: config.c_files) {
|
||||
files+=std::format(" {}_source.o ",(buildDir/cFile.filename()).string());
|
||||
if(!fs::exists((buildDir/cFile.filename()).string()+"_source.o") || fs::last_write_time(cFile.string()+".c") > fs::last_write_time((buildDir/cFile.filename()).string()+"_source.o")) {
|
||||
threads.emplace_back(&RunClang, std::format("clang {}.c -c -o {}_source.o", cFile.string(), (buildDir/cFile.filename()).string()));
|
||||
}
|
||||
}
|
||||
|
||||
for(const fs::path& cFile: config.cuda) {
|
||||
files+=std::format(" {}_source.o ",(buildDir/cFile.filename()).string());
|
||||
if(!fs::exists((buildDir/cFile.filename()).string()+"_source.o") || fs::last_write_time(cFile.string()+".cu") > fs::last_write_time((buildDir/cFile.filename()).string()+"_source.o")) {
|
||||
threads.emplace_back(&RunClang, std::format("nvcc {}.cu -c -o {}_source.o -O3 -arch=sm_89", cFile.string(), (buildDir/cFile.filename()).string()));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> resultInterfaces(config.interfaces.size()*2);
|
||||
|
||||
for(uint_fast32_t i = 0; i < config.interfaces.size(); i++) {
|
||||
if(config.interfaces[i]->Check(pcmDir)) {
|
||||
threads.emplace_back(&Module::Compile, config.interfaces[i].get(), command, pcmDir, buildDir, std::ref(resultInterfaces[i]));
|
||||
threads.emplace_back(&Module::CompileSource, config.interfaces[i].get(), command, pcmDir, buildDir, std::ref(resultInterfaces[config.interfaces.size()+i]));
|
||||
buildResult.repack = true;
|
||||
}
|
||||
files += std::format(" {}/{}.o", buildDir.string(), config.interfaces[i]->path.filename().string());
|
||||
for(std::unique_ptr<ModulePartition>& part : config.interfaces[i]->partitions) {
|
||||
files += std::format(" {}/{}.o", buildDir.string(), part->path.filename().string());
|
||||
}
|
||||
}
|
||||
std::vector<std::string> resultImplementations(config.implementations.size());
|
||||
|
||||
for(uint_fast32_t i = 0; i < config.implementations.size(); i++) {
|
||||
if(config.implementations[i].Check(buildDir, pcmDir)) {
|
||||
buildResult.repack = true;
|
||||
threads.emplace_back(&Implementation::Compile, &config.implementations[i], command, buildDir, std::ref(resultImplementations[i]));
|
||||
}
|
||||
files += std::format(" {}/{}_impl.o", buildDir.string(), config.implementations[i].path.filename().string());
|
||||
}
|
||||
|
||||
for(std::thread& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
for(const std::string& result2 : resultInterfaces) {
|
||||
buildResult.errors += result2;
|
||||
}
|
||||
for(const std::string& result2 : resultImplementations) {
|
||||
buildResult.errors += result2;
|
||||
}
|
||||
|
||||
if(!buildResult.errors.empty()) {
|
||||
fileThread.join();
|
||||
return buildResult;
|
||||
}
|
||||
|
||||
if(config.target != "wasm32-wasi") {
|
||||
command += " -L/usr/local/lib";
|
||||
}
|
||||
|
||||
command += std::format(" -L{}", buildDir.string());
|
||||
|
||||
if(config.type != CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
||||
command += libsString;
|
||||
}
|
||||
|
||||
for(const std::string& lib : config.libs) {
|
||||
depLibSet.insert(lib);
|
||||
command += std::format(" -l{}", lib);
|
||||
}
|
||||
|
||||
for(const std::string& dir : config.libDirs) {
|
||||
command += std::format(" -L{}", dir);
|
||||
}
|
||||
|
||||
fileThread.join();
|
||||
|
||||
if(config.dependencies.size() > 0){
|
||||
command += std::format(" -L{}", pcmDir.string());
|
||||
}
|
||||
|
||||
if(config.target == "x86_64-w64-mingw32"){
|
||||
command += std::format(" -L{}", binDir.string());
|
||||
}
|
||||
|
||||
if(buildResult.repack) {
|
||||
if(config.type == CRAFTER_CONFIGURATION_TYPE_EXECUTABLE) {
|
||||
if(config.target == "wasm32-wasi") {
|
||||
outputName += ".wasm";
|
||||
}//
|
||||
|
||||
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
buildResult.errors = RunClang(std::format("{}{} -o {} -fuse-ld=lld ", command, files, (binDir/outputName).string()));
|
||||
#endif
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
system(std::format("copy \"%LIBCXX_DIR%\\lib\\c++.dll\" \"{}\\c++.dll\"", binDir.string()).c_str());
|
||||
buildResult.errors = RunClang(std::format("{}{} -o {}.exe -fuse-ld=lld -L %LIBCXX_DIR%\\lib -lc++ -nostdinc++ -nostdlib++", command, files, (binDir/outputName).string()));
|
||||
#endif
|
||||
} else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
RunClang(std::format("ar r {}.a {}", (binDir/fs::path(std::string("lib")+outputName)).string(), files));
|
||||
#endif
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
RunClang(std::format("llvm-lib.exe {} /OUT:{}.lib", files, (binDir/fs::path(outputName)).string()));
|
||||
#endif
|
||||
} else {
|
||||
buildResult.errors = RunClang(std::format("{}{} -shared -o {}.so -Wl,-rpath,'$ORIGIN' -fuse-ld=lld", command, files, (binDir/(std::string("lib")+outputName)).string()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return buildResult;
|
||||
}
|
||||
|
||||
|
||||
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) const {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
fs::path binDir = path/this->binDir/test.config.name;
|
||||
fs::path buildDir = path/this->buildDir/test.config.name;
|
||||
|
||||
BuildResult buildResult = Build(test.config, binDir, binDir, buildDir, test.config.name);
|
||||
|
||||
if(!buildResult.errors.empty()) {
|
||||
|
||||
return {test.config.name, buildResult.errors};
|
||||
}
|
||||
|
||||
std::string lib = std::format("{}/lib{}.so", binDir.string(), test.config.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) {
|
||||
std::string msg = std::string(dlsym_error);
|
||||
dlclose(handle);
|
||||
throw std::runtime_error(std::format("Cannot load symbol 'RunTest': {}", msg));
|
||||
}
|
||||
std::string* testResult;
|
||||
try {
|
||||
testResult = loadedTest();
|
||||
} catch(std::exception& e) {
|
||||
return {test.config.name, std::string(e.what())};
|
||||
}
|
||||
if(testResult != nullptr) {
|
||||
TestResult result = {test.config.name, *testResult};
|
||||
delete testResult;
|
||||
return result;
|
||||
} else {
|
||||
return {test.config.name, ""};
|
||||
}
|
||||
return {test.config.name, ""};
|
||||
#else
|
||||
return {test.config.name, "Tests are not supported on this platform"};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ namespace Crafter {
|
|||
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 {
|
||||
std::string 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);
|
||||
|
|
@ -89,12 +89,12 @@ namespace Crafter {
|
|||
|
||||
spv::SpvBuildLogger logger;
|
||||
std::vector<std::uint32_t> spirv;
|
||||
std::cout << info_log;
|
||||
glslang::GlslangToSpv(*intermediate, spirv, &logger);
|
||||
std::cout << logger.getAllMessages();
|
||||
std::string errorLog = 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));
|
||||
return errorLog;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
Crafter® Build
|
||||
Copyright (C) 2026 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, const Project& project): config(configs, config, workingDir, project) {
|
||||
this->config.type = CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
Crafter.Build
|
||||
Copyright (C) 2026 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>
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
import Crafter.Build;
|
||||
import std;
|
||||
using namespace Crafter;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
void terminateHandler() {
|
||||
try {
|
||||
if (auto e = std::current_exception()) {
|
||||
std::rethrow_exception(e);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
std::abort();
|
||||
}
|
||||
#endif//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
std::set_terminate(terminateHandler);
|
||||
#endif
|
||||
if(std::string(argv[1]) == "--help" || std::string(argv[1]) == "help" ) {
|
||||
std::println("Crafter.Build - A modern build system for C++ projects\n");
|
||||
std::println("Usage: crafter-build [command] [options]");
|
||||
std::println("\nCommands:");
|
||||
std::println(" build\t\tBuild the project");
|
||||
std::println(" test\t\tRun project tests");
|
||||
std::println("\nOptions:");
|
||||
std::println(" --help\tDisplay this help message");
|
||||
std::println(" -p PATH\tSpecify the project file path (default: project.json)");
|
||||
std::println(" -r\t\tRun the built 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 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 == "-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(std::move(projectPath));
|
||||
|
||||
if(command == "build") {
|
||||
std::tuple<Configuration&, BuildResult> config = project.Build(argument);
|
||||
try {
|
||||
if(std::get<1>(config).errors.empty()) {
|
||||
if(run){
|
||||
std::string binDir = std::format("{}/{}", project.binDir.string(), std::get<0>(config).name);
|
||||
if(std::get<0>(config).target == "x86_64-w64-mingw32") {
|
||||
if(std::get<0>(config).debug) {
|
||||
system(std::format("cd {} && wine ./{}.exe", (project.path.parent_path()/binDir).string(), project.name).c_str());
|
||||
//system(std::format("cd {} && lldb -o run {}", (fs::path(projectPath).parent_path()/binDir).string(), project.name).c_str());
|
||||
} else {
|
||||
system(std::format("cd {} && wine ./{}.exe", (project.path.parent_path()/binDir).string(), project.name).c_str());
|
||||
}
|
||||
} else {
|
||||
if(std::get<0>(config).debug) {
|
||||
system(std::format("cd {} && ./{}", (project.path.parent_path()/binDir).string(), project.name).c_str());
|
||||
//system(std::format("cd {} && lldb -o run {}", (fs::path(projectPath).parent_path()/binDir).string(), project.name).c_str());
|
||||
} else {
|
||||
system(std::format("cd {} && ./{}", (project.path.parent_path()/binDir).string(), project.name).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cout << std::get<1>(config).errors << std::endl;
|
||||
return 1;
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
} else if(command == "test") {
|
||||
bool anyFailed = false;
|
||||
|
||||
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;
|
||||
anyFailed = true;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
anyFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return anyFailed ? 1 : 0;
|
||||
} else {
|
||||
std::println("Unkown command: {}", command);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue