2025-10-31 16:50:47 +01:00
|
|
|
/*
|
|
|
|
|
Crafter® Build
|
|
|
|
|
Copyright (C) 2025 Catcrafts®
|
|
|
|
|
Catcrafts.net
|
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
License version 3.0 as published by the Free Software Foundation;
|
|
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
License along with this library; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
*/
|
|
|
|
|
module;
|
|
|
|
|
#include "../lib/json.hpp"
|
2026-03-01 07:42:04 +01:00
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
2025-10-31 16:50:47 +01:00
|
|
|
#include <dlfcn.h>
|
2026-03-01 07:42:04 +01:00
|
|
|
#endif
|
2025-11-01 06:50:41 +01:00
|
|
|
#include <stdlib.h>
|
2025-10-31 16:50:47 +01:00
|
|
|
module Crafter.Build:Project_impl;
|
|
|
|
|
import :Project;
|
|
|
|
|
import std;
|
|
|
|
|
import :Configuration;
|
|
|
|
|
import :Command;
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
|
|
|
|
namespace Crafter {
|
2025-11-16 18:44:41 +01:00
|
|
|
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);
|
2026-03-01 15:35:26 +01:00
|
|
|
nlohmann::json data;
|
|
|
|
|
try {
|
|
|
|
|
data = nlohmann::json::parse(f);
|
|
|
|
|
} catch(std::exception& e) {
|
|
|
|
|
std::cout << e.what() << std::endl;
|
|
|
|
|
}
|
2025-11-16 18:44:41 +01:00
|
|
|
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")) {
|
2025-11-26 17:55:52 +01:00
|
|
|
binDir = this->path/data["bin_dir"].get<std::string>();
|
2025-11-16 18:44:41 +01:00
|
|
|
} else {
|
2025-11-26 17:55:52 +01:00
|
|
|
binDir = this->path/"bin";
|
2025-11-16 18:44:41 +01:00
|
|
|
}
|
|
|
|
|
if(data.contains("build_dir")) {
|
2025-11-26 17:55:52 +01:00
|
|
|
buildDir = this->path/data["build_dir"].get<std::string>();
|
2025-11-16 18:44:41 +01:00
|
|
|
} else {
|
2025-11-26 17:55:52 +01:00
|
|
|
buildDir = this->path/"build";
|
2025-11-16 18:44:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)) {
|
2025-12-04 11:48:33 +01:00
|
|
|
throw std::runtime_error(std::format("Project file: {} not found.", this->path.generic_string()));
|
2025-11-16 18:44:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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:;
|
|
|
|
|
}
|
2025-10-31 16:50:47 +01:00
|
|
|
Project::Project(std::string&& name, fs::path&& path, std::vector<Configuration>&& configurations) : name(std::move(name)), path(std::move(path)), configurations(std::move(configurations)) {}
|
2025-11-09 20:08:23 +01:00
|
|
|
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)) {}
|
2025-10-31 16:50:47 +01:00
|
|
|
|
2025-11-15 19:20:33 +01:00
|
|
|
|
|
|
|
|
std::tuple<Configuration&, BuildResult> Project::Build(std::string_view configuration) {
|
2025-10-31 16:50:47 +01:00
|
|
|
for(Configuration& config : configurations) {
|
|
|
|
|
if(config.name == configuration){
|
2025-11-15 19:20:33 +01:00
|
|
|
return {config, Build(config)};
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throw std::runtime_error(std::format("Configuration: {} not found.", configuration));
|
|
|
|
|
}
|
2025-11-15 19:20:33 +01:00
|
|
|
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) {
|
2025-10-31 16:50:47 +01:00
|
|
|
for(Configuration& config : configurations) {
|
|
|
|
|
if(config.name == configuration){
|
2025-11-15 19:20:33 +01:00
|
|
|
return {config, Build(config)};
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throw std::runtime_error(std::format("Configuration: {} not found.", configuration));
|
|
|
|
|
}
|
2025-11-15 19:20:33 +01:00
|
|
|
BuildResult Project::Build(Configuration& config) const {
|
|
|
|
|
return Build(config, binDir/config.name, binDir/config.name, buildDir/config.name, name);
|
2025-11-01 06:50:41 +01:00
|
|
|
}
|
2025-11-16 18:44:41 +01:00
|
|
|
|
|
|
|
|
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) {
|
2026-03-01 14:57:47 +01:00
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
|
|
|
|
libsString += std::format(" -l{}", lib2);
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
|
|
|
|
libsString+=std::format(" {}.lib", lib2);
|
|
|
|
|
#endif
|
2025-11-16 18:44:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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) {
|
2026-03-01 14:57:47 +01:00
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
|
|
|
|
libsString += std::format(" -l{}", outputLib);
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
|
|
|
|
libsString+=std::format(" {}.lib", outputLib);
|
|
|
|
|
#endif
|
2025-11-16 18:44:41 +01:00
|
|
|
}
|
|
|
|
|
AddLibsRecursive(libsString, depLibSet, std::get<1>(dep2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 19:20:33 +01:00
|
|
|
BuildResult Project::Build(Configuration& config, const fs::path& binDir, const fs::path& outputDir, const fs::path& buildDir, std::string outputName) const {
|
|
|
|
|
BuildResult buildResult;
|
2025-11-01 06:50:41 +01:00
|
|
|
if (!fs::exists(binDir)) {
|
|
|
|
|
fs::create_directories(binDir);
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-09 20:08:23 +01:00
|
|
|
if (!fs::exists(outputDir)) {
|
|
|
|
|
fs::create_directories(outputDir);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 16:50:47 +01:00
|
|
|
if (!fs::exists(buildDir)) {
|
|
|
|
|
fs::create_directories(buildDir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::thread> threads;
|
|
|
|
|
for(const Shader& shader : config.shaders) {
|
2026-02-23 02:16:50 +01:00
|
|
|
if(!shader.Check(outputDir)) {
|
2025-11-09 20:08:23 +01:00
|
|
|
threads.emplace_back(&Shader::Compile, &shader, outputDir);
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-09 20:08:23 +01:00
|
|
|
std::thread fileThread([&config, &outputDir](){
|
2025-11-09 18:56:24 +01:00
|
|
|
for (const fs::path& additionalFile : config.additionalFiles) {
|
2025-11-09 20:08:23 +01:00
|
|
|
fs::path destination = outputDir / additionalFile.filename();
|
2025-11-09 18:56:24 +01:00
|
|
|
|
|
|
|
|
if (fs::is_directory(additionalFile)) {
|
2026-02-23 02:16:50 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-09 18:56:24 +01:00
|
|
|
}
|
2026-02-23 02:16:50 +01:00
|
|
|
|
2025-11-09 18:56:24 +01:00
|
|
|
} else {
|
|
|
|
|
// Handle regular file
|
|
|
|
|
if (!fs::exists(destination)) {
|
2026-02-23 02:16:50 +01:00
|
|
|
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);
|
2025-11-09 18:56:24 +01:00
|
|
|
}
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-03-01 07:42:04 +01:00
|
|
|
|
|
|
|
|
fs::path exeDir = GetPath();
|
|
|
|
|
|
|
|
|
|
BuildStdPcm(*this, config);
|
|
|
|
|
|
|
|
|
|
std::string editedTarget = config.target;
|
|
|
|
|
std::replace(editedTarget.begin(), editedTarget.end(), '-', '_');
|
|
|
|
|
|
2026-03-02 22:20:37 +01:00
|
|
|
|
|
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
2026-03-02 21:30:19 +01:00
|
|
|
std::string command = std::format("clang++ -stdlib=libc++ --target={} -march={} -mtune={} -std={} -D CRAFTER_BUILD_CONFIGURATION_TARGET_{} -fprebuilt-module-path={}", config.target, config.march, config.march, config.standard, editedTarget, (exeDir/config.target).string());
|
2026-03-02 22:20:37 +01:00
|
|
|
#endif
|
|
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
2026-03-02 22:22:35 +01:00
|
|
|
std::string command = std::format("clang++ -nostdinc++ -nostdlib++ -isystem %LIBCXX_DIR%\\include\\c++\\v1 --target={} -march={} -mtune={} -std={} -D CRAFTER_BUILD_CONFIGURATION_TARGET_{} -fprebuilt-module-path={}", config.target, config.march, config.march, config.standard, editedTarget, (exeDir/config.target).string());
|
2026-03-02 22:20:37 +01:00
|
|
|
#endif
|
2026-03-02 21:30:19 +01:00
|
|
|
|
2026-03-02 22:04:07 +01:00
|
|
|
for (const auto& entry : fs::recursive_directory_iterator(buildDir)) {
|
|
|
|
|
if (entry.is_directory() && entry.path().filename() == "include") {
|
2026-03-02 21:30:19 +01:00
|
|
|
command += " -I" + entry.path().string() + " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 22:04:07 +01:00
|
|
|
for (const auto& entry : fs::recursive_directory_iterator(exeDir / "cloneCache")) {
|
|
|
|
|
if (entry.is_directory() && entry.path().filename() == "include") {
|
2026-03-02 21:30:19 +01:00
|
|
|
command += " -I" + entry.path().string() + " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-01 07:42:04 +01:00
|
|
|
|
2026-03-02 22:32:07 +01:00
|
|
|
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() + " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 07:42:04 +01:00
|
|
|
if(config.target == "wasm32-wasi") {
|
|
|
|
|
command += std::format(" --sysroot={} -fno-exceptions -fno-c++-static-destructors", (exeDir/"wasi-sysroot-28.0").string());
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
2026-03-01 07:42:04 +01:00
|
|
|
|
2026-03-02 21:18:11 +01:00
|
|
|
|
|
|
|
|
|
2026-03-01 15:00:13 +01:00
|
|
|
for(const Define& define : config.defines) {
|
|
|
|
|
if(define.value.empty()) {
|
2025-11-26 17:55:52 +01:00
|
|
|
command += std::format(" -D {}", define.name);
|
|
|
|
|
} else {
|
|
|
|
|
command += std::format(" -D {}={}", define.name, define.value);
|
|
|
|
|
}
|
2026-03-01 14:57:47 +01:00
|
|
|
}
|
2025-10-31 16:50:47 +01:00
|
|
|
|
|
|
|
|
fs::path pcmDir;
|
|
|
|
|
|
|
|
|
|
if(config.type == CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY) {
|
2026-03-01 14:57:47 +01:00
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
2025-11-01 06:50:41 +01:00
|
|
|
command += " -fPIC -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
2026-03-01 14:57:47 +01:00
|
|
|
#endif
|
|
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
2026-03-02 17:29:03 +01:00
|
|
|
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
2026-03-01 14:57:47 +01:00
|
|
|
#endif
|
2025-11-01 06:50:41 +01:00
|
|
|
pcmDir = binDir;
|
2025-10-31 16:50:47 +01:00
|
|
|
} else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
|
|
|
|
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_LIBRARY";
|
2025-11-01 06:50:41 +01:00
|
|
|
pcmDir = binDir;
|
2025-10-31 16:50:47 +01:00
|
|
|
} else {
|
|
|
|
|
command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_EXECUTABLE";
|
|
|
|
|
pcmDir = buildDir;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 21:18:11 +01:00
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
2026-03-02 22:36:56 +01:00
|
|
|
command += " -Wno-deprecated-declarations";
|
2026-03-02 21:18:11 +01:00
|
|
|
#endif
|
|
|
|
|
|
2026-03-01 07:42:04 +01:00
|
|
|
command += std::format(" -fprebuilt-module-path={}", pcmDir.string());
|
2026-02-23 20:59:38 +01:00
|
|
|
|
2026-03-02 15:24:45 +01:00
|
|
|
std::string cmakeBuildType;
|
|
|
|
|
|
2025-10-31 16:50:47 +01:00
|
|
|
if(config.debug) {
|
2026-03-02 15:24:45 +01:00
|
|
|
cmakeBuildType = "Debug";
|
2025-10-31 16:50:47 +01:00
|
|
|
command += " -g -D CRAFTER_BUILD_CONFIGURATION_DEBUG";
|
|
|
|
|
} else {
|
2026-03-02 15:24:45 +01:00
|
|
|
cmakeBuildType = "Release";
|
2025-10-31 16:50:47 +01:00
|
|
|
command += " -O3";
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-06 19:36:22 +01:00
|
|
|
for(const std::string& dir : config.includeDirs) {
|
|
|
|
|
command += std::format(" -I{}", dir);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 16:50:47 +01:00
|
|
|
std::unordered_set<std::string> depLibSet;
|
|
|
|
|
std::vector<std::thread> depThreads = std::vector<std::thread>(config.dependencies.size());
|
|
|
|
|
std::mutex libMutex;
|
2026-03-01 14:57:47 +01:00
|
|
|
std::string files;
|
2025-10-31 16:50:47 +01:00
|
|
|
std::string libsString;
|
2026-03-01 14:57:47 +01:00
|
|
|
|
2025-11-15 19:20:33 +01:00
|
|
|
std::vector<std::string> resultsDep(config.dependencies.size());
|
2025-10-31 16:50:47 +01:00
|
|
|
|
|
|
|
|
for(std::uint_fast32_t i = 0; i < depThreads.size(); i++) {
|
2025-11-15 19:20:33 +01:00
|
|
|
depThreads[i] = std::thread([i, &config, &libMutex, &depLibSet, &buildDir, &pcmDir, &libsString, &binDir, this, &buildResult, &resultsDep](){
|
2026-02-23 20:59:38 +01:00
|
|
|
for(Configuration& config2 : std::get<0>(config.dependencies[i])->configurations) {
|
|
|
|
|
config2.target = config.target;
|
2026-02-24 03:34:34 +01:00
|
|
|
config2.march = config.march;
|
2026-02-23 20:59:38 +01:00
|
|
|
}
|
2025-11-16 18:44:41 +01:00
|
|
|
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;
|
2025-11-01 11:53:09 +01:00
|
|
|
}
|
2025-11-16 18:44:41 +01:00
|
|
|
resultsDep[i] = depResult.errors;
|
|
|
|
|
|
|
|
|
|
if (depLibSet.insert(outputLib).second) {
|
|
|
|
|
libsString+=std::format(" -l{}", outputLib);
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
2025-11-16 18:44:41 +01:00
|
|
|
AddLibsRecursive(libsString, depLibSet, std::get<1>(config.dependencies[i]));
|
|
|
|
|
libMutex.unlock();
|
2025-10-31 16:50:47 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 15:24:45 +01:00
|
|
|
for(const CmakeDep& cmake : config.cmakeDeps) {
|
|
|
|
|
depThreads.emplace_back([&cmake, &cmakeBuildType, &buildDir](){
|
2026-03-02 22:04:07 +01:00
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
2026-03-02 15:24:45 +01:00
|
|
|
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, buildDir.string(), buildDir.string(), cmake.options, cmakeBuildType).c_str());
|
2026-03-02 22:04:07 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
2026-03-02 22:28:03 +01:00
|
|
|
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, buildDir.string(), buildDir.string(), cmake.options, cmakeBuildType).c_str());
|
2026-03-02 22:04:07 +01:00
|
|
|
#endif
|
2026-03-02 15:24:45 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 16:50:47 +01:00
|
|
|
for(std::thread& thread : depThreads) {
|
|
|
|
|
thread.join();
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 19:20:33 +01:00
|
|
|
for(const std::string& result2 : resultsDep) {
|
|
|
|
|
buildResult.errors += result2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!buildResult.errors.empty()) {
|
|
|
|
|
fileThread.join();
|
|
|
|
|
return buildResult;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 20:24:35 +01:00
|
|
|
|
2025-11-16 18:44:41 +01:00
|
|
|
for(const fs::path& cFile: config.c_files) {
|
2026-01-06 19:36:22 +01:00
|
|
|
files+=std::format(" {}_source.o ",(buildDir/cFile.filename()).string());
|
2025-11-16 18:44:41 +01:00
|
|
|
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()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-06 19:36:22 +01:00
|
|
|
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")) {
|
2026-02-23 02:16:50 +01:00
|
|
|
threads.emplace_back(&RunClang, std::format("nvcc {}.cu -c -o {}_source.o -O3 -arch=sm_89", cFile.string(), (buildDir/cFile.filename()).string()));
|
2026-01-06 19:36:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 17:29:03 +01:00
|
|
|
std::vector<std::string> resultInterfaces(config.interfaces.size()*2);
|
2025-11-15 19:20:33 +01:00
|
|
|
|
|
|
|
|
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]));
|
2026-03-02 17:29:03 +01:00
|
|
|
threads.emplace_back(&Module::CompileSource, config.interfaces[i].get(), command, pcmDir, buildDir, std::ref(resultInterfaces[config.interfaces.size()+i]));
|
2025-11-15 19:20:33 +01:00
|
|
|
buildResult.repack = true;
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
2025-11-15 19:20:33 +01:00
|
|
|
files += std::format(" {}/{}.o", buildDir.string(), config.interfaces[i]->path.filename().string());
|
|
|
|
|
for(std::unique_ptr<ModulePartition>& part : config.interfaces[i]->partitions) {
|
2025-10-31 16:50:47 +01:00
|
|
|
files += std::format(" {}/{}.o", buildDir.string(), part->path.filename().string());
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-15 19:20:33 +01:00
|
|
|
std::vector<std::string> resultImplementations(config.implementations.size());
|
2025-10-31 16:50:47 +01:00
|
|
|
|
2025-11-15 19:20:33 +01:00
|
|
|
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]));
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
2025-11-15 19:20:33 +01:00
|
|
|
files += std::format(" {}/{}_impl.o", buildDir.string(), config.implementations[i].path.filename().string());
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(std::thread& thread : threads) {
|
|
|
|
|
thread.join();
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 19:20:33 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:56:24 +01:00
|
|
|
if(config.target != "wasm32-wasi") {
|
|
|
|
|
command += " -L/usr/local/lib";
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 15:24:45 +01:00
|
|
|
command += std::format(" -L{}", buildDir.string());
|
|
|
|
|
|
2026-03-01 15:06:36 +01:00
|
|
|
if(config.type != CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
|
|
|
|
command += libsString;
|
|
|
|
|
}
|
2025-10-31 16:50:47 +01:00
|
|
|
|
|
|
|
|
for(const std::string& lib : config.libs) {
|
|
|
|
|
depLibSet.insert(lib);
|
|
|
|
|
command += std::format(" -l{}", lib);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-06 19:36:22 +01:00
|
|
|
for(const std::string& dir : config.libDirs) {
|
|
|
|
|
command += std::format(" -L{}", dir);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 16:50:47 +01:00
|
|
|
fileThread.join();
|
|
|
|
|
|
|
|
|
|
if(config.dependencies.size() > 0){
|
|
|
|
|
command += std::format(" -L{}", pcmDir.string());
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 19:20:33 +01:00
|
|
|
if(buildResult.repack) {
|
2026-03-01 14:57:47 +01:00
|
|
|
if(config.type == CRAFTER_CONFIGURATION_TYPE_EXECUTABLE) {
|
2025-11-09 20:08:23 +01:00
|
|
|
if(config.target == "wasm32-wasi") {
|
|
|
|
|
outputName += ".wasm";
|
2026-03-02 22:49:51 +01:00
|
|
|
}//
|
2026-03-02 22:20:37 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
2026-03-02 15:24:45 +01:00
|
|
|
buildResult.errors = RunClang(std::format("{}{} -o {} -fuse-ld=lld ", command, files, (binDir/outputName).string()));
|
2026-03-02 22:20:37 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
2026-03-02 23:08:17 +01:00
|
|
|
system(std::format("copy \"%LIBCXX_DIR%\\lib\\c++.dll\" \"{}\\c++.dll\"", binDir.string()).c_str());
|
2026-03-02 22:51:14 +01:00
|
|
|
buildResult.errors = RunClang(std::format("{}{} -o {}.exe -fuse-ld=lld -L %LIBCXX_DIR%\\lib -lc++ -nostdinc++ -nostdlib++", command, files, (binDir/outputName).string()));
|
2026-03-02 22:20:37 +01:00
|
|
|
#endif
|
2026-03-01 14:57:47 +01:00
|
|
|
} else if(config.type == CRAFTER_CONFIGURATION_TYPE_LIBRARY) {
|
|
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
2026-03-02 22:55:42 +01:00
|
|
|
RunClang(std::format("ar r {}.a {}", (binDir/fs::path(std::string("lib")+outputName)).string(), files));
|
2026-03-01 14:57:47 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
2026-03-02 22:14:56 +01:00
|
|
|
RunClang(std::format("clang-lib.exe {} /OUT:{}.lib", files, (binDir/fs::path(outputName)).string()));
|
2026-03-01 14:57:47 +01:00
|
|
|
#endif
|
2025-10-31 16:50:47 +01:00
|
|
|
} else {
|
2025-11-15 19:20:33 +01:00
|
|
|
buildResult.errors = RunClang(std::format("{}{} -shared -o {}.so -Wl,-rpath,'$ORIGIN' -fuse-ld=lld", command, files, (binDir/(std::string("lib")+outputName)).string()));
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-11-15 19:20:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
return buildResult;
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-01 06:50:41 +01:00
|
|
|
TestResult Project::RunTest(Test& test) const {
|
2026-03-01 07:42:04 +01:00
|
|
|
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
2025-11-09 20:08:23 +01:00
|
|
|
fs::path binDir = path/this->binDir/test.config.name;
|
|
|
|
|
fs::path buildDir = path/this->buildDir/test.config.name;
|
2025-11-01 06:50:41 +01:00
|
|
|
|
2025-11-15 19:22:32 +01:00
|
|
|
BuildResult buildResult = Build(test.config, binDir, binDir, buildDir, test.config.name);
|
|
|
|
|
|
|
|
|
|
if(!buildResult.errors.empty()) {
|
2025-11-16 18:44:41 +01:00
|
|
|
|
2025-11-15 19:22:32 +01:00
|
|
|
return {test.config.name, buildResult.errors};
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
2025-11-01 06:50:41 +01:00
|
|
|
|
|
|
|
|
std::string lib = std::format("{}/lib{}.so", binDir.string(), test.config.name);
|
2025-10-31 16:50:47 +01:00
|
|
|
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) {
|
2025-11-01 06:50:41 +01:00
|
|
|
std::string msg = std::string(dlsym_error);
|
2025-10-31 16:50:47 +01:00
|
|
|
dlclose(handle);
|
2025-11-01 06:50:41 +01:00
|
|
|
throw std::runtime_error(std::format("Cannot load symbol 'RunTest': {}", msg));
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
2025-11-03 15:52:04 +01:00
|
|
|
std::string* testResult;
|
|
|
|
|
try {
|
|
|
|
|
testResult = loadedTest();
|
|
|
|
|
} catch(std::exception& e) {
|
|
|
|
|
return {test.config.name, std::string(e.what())};
|
|
|
|
|
}
|
2025-10-31 16:50:47 +01:00
|
|
|
if(testResult != nullptr) {
|
|
|
|
|
TestResult result = {test.config.name, *testResult};
|
|
|
|
|
delete testResult;
|
|
|
|
|
return result;
|
|
|
|
|
} else {
|
|
|
|
|
return {test.config.name, ""};
|
|
|
|
|
}
|
2025-11-01 06:50:41 +01:00
|
|
|
return {test.config.name, ""};
|
2026-03-01 07:42:04 +01:00
|
|
|
#else
|
|
|
|
|
return {test.config.name, "Tests are not supported on this platform"};
|
|
|
|
|
#endif
|
2025-10-31 16:50:47 +01:00
|
|
|
}
|
|
|
|
|
}
|