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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue