Initial LLVM build

This commit is contained in:
TennesseeTrash 2025-12-30 22:18:27 +01:00
parent cdfbcf4f61
commit 4a35de9a69
10 changed files with 342 additions and 23 deletions

View file

@ -22,6 +22,7 @@ target_link_libraries(Toolchain
target_sources(Toolchain target_sources(Toolchain
PRIVATE PRIVATE
"Main.cpp" "Main.cpp"
"Source/Common.cpp"
"Source/Command.cpp" "Source/Command.cpp"
"Source/Download.cpp" "Source/Download.cpp"
"Source/Package.cpp" "Source/Package.cpp"

View file

@ -0,0 +1,41 @@
#ifndef TOOLCHAIN_COMMAND_HPP
#define TOOLCHAIN_COMMAND_HPP
#include "Common.hpp"
#include <vector>
namespace Toolchain
{
class Command
{
public:
Command();
virtual ~Command() = default;
virtual bool Run() = 0;
};
class CMakeCommand: public Command
{
public:
private:
fs::path mSource;
fs::path mBuild;
fs::path mInstall;
std::vector<std::pair<std::string, std::string>> mOptions;
};
class NinjaCommand: public Command
{
public:
private:
fs::path mBuild;
};
}
#endif // TOOLCHAIN_COMMAND_HPP

View file

@ -2,11 +2,22 @@
#define TOOLCHAIN_COMMON_HPP #define TOOLCHAIN_COMMON_HPP
#include <filesystem> // IWYU pragma: export #include <filesystem> // IWYU pragma: export
#include <string_view>
namespace Toolchain namespace Toolchain
{ {
// NOLINTNEXTLINE: We're defining this to make life easier. // NOLINTNEXTLINE: We're defining this to make life easier.
namespace fs = std::filesystem; namespace fs = std::filesystem;
enum class FileType
{
Unknown, Zip, Tar,
};
// Note(3011): This function should handle both system paths, and URLs.
FileType DetectFileType(std::string_view file);
std::string_view ToString(FileType fileType);
} }
#endif // TOOLCHAIN_COMMON_HPP #endif // TOOLCHAIN_COMMON_HPP

View file

@ -6,6 +6,8 @@
namespace Toolchain namespace Toolchain
{ {
bool DownloadFile(const fs::path &path, std::string_view url); bool DownloadFile(const fs::path &path, std::string_view url);
// TODO(3011): Archive verification ...
} }
#endif // TOOLCHAIN_DOWNLOAD_HPP #endif // TOOLCHAIN_DOWNLOAD_HPP

View file

@ -1,6 +1,9 @@
#ifndef TOOLCHAIN_PACKAGE_HPP #ifndef TOOLCHAIN_PACKAGE_HPP
#define TOOLCHAIN_PACKAGE_HPP #define TOOLCHAIN_PACKAGE_HPP
#include <string>
#include <vector>
namespace Toolchain namespace Toolchain
{ {
// The package should have a name, version, source url, and it should describe // The package should have a name, version, source url, and it should describe
@ -25,9 +28,16 @@ namespace Toolchain
// with all the other projects. It will still require system dependencies for certain bits, but // with all the other projects. It will still require system dependencies for certain bits, but
// that doesn't matter too much, as long as the host has a reasonably complete package registry. // that doesn't matter too much, as long as the host has a reasonably complete package registry.
using Step = std::vector<std::string>;
class Package class Package
{ {
public:
Package();
private:
std::string mName;
std::string mUrl;
}; };
} }

View file

@ -0,0 +1,23 @@
#ifndef TOOLCHAIN_STAGE_HPP
#define TOOLCHAIN_STAGE_HPP
#include "Common.hpp"
#include "Package.hpp"
namespace Toolchain
{
class Stage
{
public:
Stage(const fs::path &path);
void AddPackage(const Package &package);
void Build();
private:
fs::path mDirectory;
};
}
#endif // TOOLCHAIN_STAGE_HPP

View file

@ -1,12 +1,83 @@
#include "Common.hpp"
#include "Download.hpp" #include "Download.hpp"
#include "Unpack.hpp"
#include <Process/Environment.hpp>
#include <Process/Process.hpp>
#include <filesystem>
#include <print> #include <print>
int main() int main()
{ {
std::println("Hello World!"); std::println("Hello World!");
if (!Toolchain::DownloadFile("index.html", "https://3011.io/")) { Toolchain::fs::create_directories("Archives");
if (!Toolchain::fs::exists("Archives/LLVM.tar.gz") && !Toolchain::DownloadFile("Archives/LLVM.tar.gz", "https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-21.1.8.tar.gz")) {
std::println("Unlucky"); std::println("Unlucky");
return 1;
}
Toolchain::fs::create_directories("Sources");
if (!Toolchain::fs::exists("Sources/LLVM") && !Toolchain::UnpackArchive("Archives/LLVM.tar.gz", "Sources/LLVM")) {
std::println("Unlucky");
return 1;
}
return 0;
Toolchain::fs::create_directories("Builds");
Process::Process configure(
"cmake", { "-S", "Sources/LLVM/llvm", "-B", "Builds/LLVM", "-G", "Ninja", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_INSTALL_PREFIX=Stage0", "-DLLVM_ENABLE_PROJECTS=all", "-DLLVM_ENABLE_RUNTIMES=all" },
Process::ProcessOptions {
.StdinMode = Process::PipeMode::Null,
.StdoutMode = Process::PipeMode::Inherit,
.StderrMode = Process::PipeMode::Inherit,
.PathResolution = Process::PathMode::CurrentEnvironment,
.Environment = Process::CurrentEnvironment(),
.CloseOtherFds = true,
}
);
configure.Wait();
if (configure.ExitCode() != 0) {
std::println("Unlucky");
return 1;
}
Process::Process build (
"ninja", { "-C", "Builds/LLVM", "-j4" },
Process::ProcessOptions {
.StdinMode = Process::PipeMode::Null,
.StdoutMode = Process::PipeMode::Inherit,
.StderrMode = Process::PipeMode::Inherit,
.PathResolution = Process::PathMode::CurrentEnvironment,
.Environment = Process::CurrentEnvironment(),
.CloseOtherFds = true,
}
);
build.Wait();
if (build.ExitCode() != 0) {
std::println("Unlucky");
return 1;
}
Process::Process install (
"ninja", { "-C", "Builds/LLVM", "install" },
Process::ProcessOptions {
.StdinMode = Process::PipeMode::Null,
.StdoutMode = Process::PipeMode::Inherit,
.StderrMode = Process::PipeMode::Inherit,
.PathResolution = Process::PathMode::CurrentEnvironment,
.Environment = Process::CurrentEnvironment(),
.CloseOtherFds = true,
}
);
install.Wait();
if (install.ExitCode() != 0){
std::println("Unlucky");
return 1;
} }
} }

View file

@ -0,0 +1,43 @@
#include "Common.hpp"
#include <print>
namespace Toolchain
{
FileType DetectFileType(std::string_view file)
{
if (std::size_t pos = file.find_last_of("?"); pos != std::string_view::npos) {
file = file.substr(0, pos);
}
if (std::size_t pos = file.find_last_of("#"); pos != std::string_view::npos) {
file = file.substr(0, pos);
}
if (std::size_t pos = file.find_last_of("/"); pos != std::string_view::npos) {
file = file.substr(pos + 1);
}
if (file.ends_with(".zip")) {
return FileType::Zip;
}
if (file.contains(".tar")) {
return FileType::Tar;
}
return FileType::Unknown;
}
std::string_view ToString(FileType fileType)
{
switch (fileType) {
case FileType::Unknown:
return "Unknown";
case FileType::Zip:
return "zip";
case FileType::Tar:
return "tar";
}
}
}

View file

@ -25,6 +25,7 @@ namespace Toolchain
{ {
bool DownloadFile(const fs::path &path, std::string_view url) bool DownloadFile(const fs::path &path, std::string_view url)
{ {
std::println("Downloading {} to {}", url, path.c_str());
using offset_t = cpr::cpr_off_t; using offset_t = cpr::cpr_off_t;
std::ofstream file(path, std::ios::binary); std::ofstream file(path, std::ios::binary);
@ -43,10 +44,38 @@ namespace Toolchain
} }
), ),
cpr::ProgressCallback( cpr::ProgressCallback(
[path] (offset_t dt, offset_t dp, offset_t ut, offset_t up, std::intptr_t) { [path] (offset_t dt, offset_t dp, offset_t, offset_t, std::intptr_t) {
if (!dt) {
std::string unit = "B";
double current = dp;
if (current >= 1024.0) {
current /= 1024.0;
unit = "KiB";
}
if (current >= 1024.0) {
current /= 1024.0;
unit = "MiB";
}
std::print("\rDownloading {}: {}{}", path.c_str(), current, unit);
}
else {
std::string unit = "B";
double current = dp;
double total = dt;
if (total >= 1024.0) {
total /= 1024.0;
current /= 1024.0;
unit = "KiB";
}
if (total >= 1024.0) {
total /= 1024.0;
current /= 1024.0;
unit = "MiB";
}
double progress = double(dp) / double(dt); double progress = double(dp) / double(dt);
std::print("\rDownloading {}: {:.2f}% ({}/{})", std::print("\rDownloading {}: {:.2f}% ({}/{}{})",
path.c_str(), progress * 100, dp, dt); path.c_str(), progress * 100, current, total, unit);
}
return true; return true;
} }
) )
@ -62,4 +91,18 @@ namespace Toolchain
file.close(); file.close();
return true; return true;
} }
std::string DownloadContent(std::string_view url)
{
std::println("Downloading {}", url);
cpr::Response response = cpr::Get(cpr::Url(url));
if (response.status_code != 200) {
std::println("Download failed with status: {}, {}",
response.status_code, response.status_line);
return {};
}
return response.text;
}
} }

View file

@ -1,33 +1,107 @@
#include "Unpack.hpp" #include "Unpack.hpp"
#include "Common.hpp"
#include <Process/Process.hpp> #include <Process/Process.hpp>
#include <filesystem>
#include <print>
namespace
{
namespace fs = std::filesystem;
bool UnpackZip(const fs::path &sourceArchive, const fs::path &destinationDirectory)
{
Process::Process unzip(
"unzip", { "-o", sourceArchive.string(), "-d", destinationDirectory.string() },
Process::ProcessOptions {
.StdinMode = Process::PipeMode::Null,
.StdoutMode = Process::PipeMode::Null,
.StderrMode = Process::PipeMode::Null,
.PathResolution = Process::PathMode::CurrentEnvironment,
.Environment = {},
.CloseOtherFds = true,
}
);
unzip.Wait();
return unzip.ExitCode() == 0;
}
bool UnpackTar(const fs::path &sourceArchive, const fs::path &destinationDirectory)
{
Process::Process untar(
"tar", { "-xf", sourceArchive.string(), "-C", destinationDirectory.string() },
Process::ProcessOptions {
.StdinMode = Process::PipeMode::Null,
.StdoutMode = Process::PipeMode::Null,
.StderrMode = Process::PipeMode::Null,
.PathResolution = Process::PathMode::CurrentEnvironment,
.Environment = {},
.CloseOtherFds = true,
}
);
untar.Wait();
return untar.ExitCode() == 0;
}
std::vector<fs::path> GetEntries(const fs::path &directory)
{
std::vector<fs::path> result;
for (const auto &entry: fs::directory_iterator(directory)) {
result.push_back(entry);
}
return result;
}
}
namespace Toolchain namespace Toolchain
{ {
bool UnpackArchive(const fs::path &sourceArchive, const fs::path &destinationDirectory) bool UnpackArchive(const fs::path &sourceArchive, const fs::path &destinationDirectory)
{ {
// Select the command based on the file extension if (fs::exists(destinationDirectory)) {
// Extract the archive into a temporary directory
// Check how many items were extracted (non-recursively, we only care about the top level)
// If there was only one item, and it's a directory, it should be moved (and renamed)
// to the destination. In case it's a file, the destination should be created, and the
// file moved there.
// If there are multiple items, the destination will be created, and all items moved into
// the destination.
// Error handling and cleanup should be done so the temporary directory is always
// kept clean in case the program tries to use it for other stuff.
// The reasoning behind this sort of behaviour is that the whole thing should always be as
// consistent as possible, we want everything to be as consistent as possible, and easy to
// work with afterwards. The point is that for the most part, the structure is know, so
// it is easy to proceed, and otherwise it should be easy to analyze the contents
// programatically.
return false; return false;
} }
FileType fileType = DetectFileType(sourceArchive.string());
if (fileType == FileType::Unknown ||
(fileType != FileType::Zip && fileType != FileType::Tar)
) {
return false;
}
fs::path stagingDirectory = destinationDirectory.parent_path() / "UnpackStaging";
if (fs::exists(stagingDirectory)) {
return false;
}
fs::create_directories(stagingDirectory);
if (fileType == FileType::Zip) {
if (!UnpackZip(sourceArchive, stagingDirectory)) {
fs::remove_all(stagingDirectory);
return false;
}
}
else if (fileType == FileType::Tar) {
if (!UnpackTar(sourceArchive, stagingDirectory)) {
fs::remove_all(stagingDirectory);
return false;
}
}
// Note(3011): Rename will fail between different filesystems.
// This situation will likely never happen for intended workloads,
// but in case it does happen, a fallback to copy + remove will be necessary.
std::vector<fs::path> entries = GetEntries(stagingDirectory);
if (entries.size() == 1 && fs::is_directory(entries[0])) {
fs::rename(entries[0], destinationDirectory);
fs::remove_all(stagingDirectory);
return true;
}
// Note(3011): The archive was either empty, or there are multiple entries,
// we don't really care at this point.
fs::rename(stagingDirectory, destinationDirectory);
return true;
}
} }